twentyhq/twenty · error · Error
Malformed workflow version: missing steps information; be su
Error message
Malformed workflow version: missing steps information; be sure to create at least one step before trying to edit one
What it means
getStepDefinitionOrThrow throws when the requested stepId is not the trigger step and the workflow version's `steps` array is null/undefined. It means the loaded WorkflowVersion has no action steps at all, so any non-trigger lookup is invalid.
Source
Thrown at packages/twenty-front/src/modules/workflow/utils/getStepDefinitionOrThrow.ts:33
trigger: WorkflowTrigger | null;
steps: Array<WorkflowAction> | null;
}) => {
if (stepId === TRIGGER_STEP_ID) {
if (!isDefined(trigger)) {
return {
type: 'trigger',
definition: undefined,
} as const;
}
return {
type: 'trigger',
definition: trigger,
} as const;
}
if (!isDefined(steps)) {
throw new Error(
'Malformed workflow version: missing steps information; be sure to create at least one step before trying to edit one',
);
}
const selectedNodePosition = findStepPosition({
steps,
stepId,
});
if (!isDefined(selectedNodePosition)) {
return undefined;
}
return {
type: 'action',
definition: selectedNodePosition.steps[selectedNodePosition.index],
} as const;
};
View on GitHub (pinned to 1f5dd2bbd2)
Solutions
- Guard the call site: skip the lookup until steps is a non-null array.
- Ensure at least one action step exists before opening the step editor.
- Verify the workflow version query selects the steps field.
Example fix
// before: const def = getStepDefinitionOrThrow({ stepId, trigger, steps });
// after: if (stepId !== TRIGGER_STEP_ID && !steps?.length) return undefined;
// const def = getStepDefinitionOrThrow({ stepId, trigger, steps }); Defensive patterns
Strategy: type-guard
Validate before calling
const canLookupStep = (stepId: string, steps: Array<WorkflowAction> | null): boolean => stepId === TRIGGER_STEP_ID || (Array.isArray(steps) && steps.length > 0);
Type guard
const hasActionSteps = (steps: Array<WorkflowAction> | null | undefined): steps is Array<WorkflowAction> => Array.isArray(steps) && steps.length > 0;
Try / catch
try {
return getStepDefinitionOrThrow({ stepId, trigger, steps });
} catch (e) {
if (/missing steps information/.test((e as Error).message)) return undefined;
throw e;
} Prevention
- Do not call getStepDefinitionOrThrow until the workflow version (with steps) has loaded.
- For new workflows with only a trigger, branch on stepId === TRIGGER_STEP_ID first.
- Ensure the workflow version query selects the steps field.
When it happens
Trigger: Editing/selecting an action step on a workflow version that only has a trigger (steps === null). The version was created as a stub before any actions were added. Race where the version fetch has not yet populated steps.
Common situations: Newly created workflow with only a trigger. Backend returned a version shape without steps. Frontend attempted to look up a step before the workflow graph finished loading.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Cron expression is required
- Failed to describe cron expression: ${errorMessage}
- Cron expression is required
- Invalid cron expression. Expected 4-6 fields, got ${parts.le
- OPERATION_FAILED
AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12).
Data as JSON: /api/errors/b85f27f3a4aecb70.
Report an issue: GitHub.