# Buggy code
def get_transitions(doc, workflow=None):
    """Return list of possible transitions for the given doc"""
    doc = frappe.get_doc(frappe.parse_json(doc))

    if doc.is_new():
        return []

    frappe.has_permission(doc, "read", throw=True)
    roles = frappe.get_roles()

    if not workflow:
        workflow = get_workflow(doc.doctype)
    current_state = doc.get(workflow.workflow_state_field)

    if not current_state:
        frappe.throw(_("Workflow State not set"), WorkflowStateError)

    transitions = []
    for transition in workflow.transitions:
        if transition.state == current_state and transition.allowed in roles:
            if transition.condition:
                # if condition, evaluate
                # access to frappe.db.get_value and frappe.db.get_list
                success = frappe.safe_eval(
                    transition.condition,
                    dict(
                        frappe=frappe._dict(
                            db=frappe._dict(
                                get_value=frappe.db.get_value,
                                get_list=frappe.db.get_list,
                            ),
                            session=frappe.session,
                        )
                    ),
                    dict(doc=doc),
                )
                if not success:
                    continue
            transitions.append(transition.as_dict())

    return transitions


# Traceback (TypeError)
Traceback (most recent call last):
File "/home/frappe/frappe-bench/apps/frappe/frappe/app.py", line 62, in application
response = frappe.handler.handle()
File "/home/frappe/frappe-bench/apps/frappe/frappe/handler.py", line 22, in handle
data = execute_cmd(cmd)
File "/home/frappe/frappe-bench/apps/frappe/frappe/handler.py", line 53, in execute_cmd
return frappe.call(method, **frappe.form_dict)
File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 942, in call
return fn(*args, **newargs)
File "/home/frappe/frappe-bench/apps/frappe/frappe/model/workflow.py", line 30, in get_transitions
frappe.has_permission(doc, 'read', throw=True)
File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 535, in has_permission
out = frappe.permissions.has_permission(doctype, ptype, doc=doc, verbose=verbose, user=user)
File "/home/frappe/frappe-bench/apps/frappe/frappe/permissions.py", line 41, in has_permission
meta = frappe.get_meta(doctype)
File "/home/frappe/frappe-bench/apps/frappe/frappe/__init__.py", line 658, in get_meta
return frappe.model.meta.get_meta(doctype, cached=cached)
File "/home/frappe/frappe-bench/apps/frappe/frappe/model/meta.py", line 34, in get_meta
lambda: Meta(doctype))
File "/home/frappe/frappe-bench/apps/frappe/frappe/utils/redis_wrapper.py", line 173, in hget
value = generator()
File "/home/frappe/frappe-bench/apps/frappe/frappe/model/meta.py", line 34, in <lambda>
lambda: Meta(doctype))
File "/home/frappe/frappe-bench/apps/frappe/frappe/model/meta.py", line 71, in __init__
super(Meta, self).__init__(doctype.as_dict())
File "/home/frappe/frappe-bench/apps/frappe/frappe/model/document.py", line 114, in __init__
self.init_valid_columns()
File "/home/frappe/frappe-bench/apps/frappe/frappe/model/base_document.py", line 232, in init_valid_columns
for key in self.get_valid_columns():
File "/home/frappe/frappe-bench/apps/frappe/frappe/model/meta.py", line 129, in get_valid_columns
[df.fieldname for df in self.get("fields") if df.fieldtype in type_map]
TypeError: 'NoneType' object is not iterable

# Fixed code
def get_transitions(doc, workflow=None):
    """Return list of possible transitions for the given doc"""
    doc = frappe.get_doc(frappe.parse_json(doc))

    if doc.is_new():
        return []

    frappe.has_permission(doc.doctype, "read", throw=True)
    roles = frappe.get_roles()

    if not workflow:
        workflow = get_workflow(doc.doctype)
    current_state = doc.get(workflow.workflow_state_field)

    if not current_state:
        frappe.throw(_("Workflow State not set"), WorkflowStateError)

    transitions = []
    for transition in workflow.transitions:
        if transition.state == current_state and transition.allowed in roles:
            if transition.condition:
                # if condition, evaluate
                # access to frappe.db.get_value and frappe.db.get_list
                success = frappe.safe_eval(
                    transition.condition,
                    dict(
                        frappe=frappe._dict(
                            db=frappe._dict(
                                get_value=frappe.db.get_value,
                                get_list=frappe.db.get_list,
                            ),
                            session=frappe.session,
                        )
                    ),
                    dict(doc=doc),
                )
                if not success:
                    continue
            transitions.append(transition.as_dict())

    return transitions