n8n-io/n8n · warning
SDK_CODE_AFTER_EXPORT_DEFAULT
SDK_CODE_AFTER_EXPORT_DEFAULT
Error message
Statement after `export default workflow(...)` never reaches the builder — nodes/wiring here are dropped. Compose all `.to()` / `.onTrue()` / `.onFalse()` / `.onCase()` inside the export default chain.
What it means
SDK_CODE_AFTER_EXPORT_DEFAULT is a lint issue. lintWorkflowSdkAst() finds the ExportDefaultDeclaration index and flags every non-EmptyStatement after it, because any node construction or wiring (.to()/.onTrue()/etc.) written as a separate top-level statement after export default is dropped — the builder only consumes the export default chain.
Source
Thrown at packages/@n8n/workflow-sdk/src/lint/sdk/workflow-sdk-lint.ts:195
lintIssue({
code: 'SDK_AS_CONST',
message:
'`as const` is TypeScript-only and the workflow parser cannot interpret it. Remove the assertion.',
line: match.line,
column: match.column + 1,
lintTarget: 'sdk',
}),
);
}
const exportIndex = ast.body.findIndex((stmt) => stmt.type === 'ExportDefaultDeclaration');
if (exportIndex >= 0) {
for (let i = exportIndex + 1; i < ast.body.length; i++) {
const stmt = ast.body[i];
if (!stmt || stmt.type === 'EmptyStatement') continue;
issues.push(
lintIssue({
code: 'SDK_CODE_AFTER_EXPORT_DEFAULT',
message:
'Statement after `export default workflow(...)` never reaches the builder — nodes/wiring here are dropped. ' +
'Compose all `.to()` / `.onTrue()` / `.onFalse()` / `.onCase()` inside the export default chain.',
...locationOf(stmt),
lintTarget: 'sdk',
}),
);
}
}
const branchCounts = new Map<string, { count: number; line?: number; column?: number }>();
walkAst(
ast,
(node, parent) => {
if (node.type === 'ImportDeclaration') return;
const forbidden = FORBIDDEN_NODE_TYPES[node.type];View on GitHub (pinned to 5ac6606e81)
Solutions
- Move all .to()/.onTrue()/.onFalse()/.onCase()/.onDone() calls inside the single export default chain.
- Delete any stray top-level statements after export default.
- Restructure so the entire workflow is one fluent expression terminating at export default.
Example fix
// before export default workflow().to(a); a.to(b); b.onTrue(c); // after export default workflow().to(a).to(b).onTrue(c);
Defensive patterns
Strategy: validation
Validate before calling
import { lintWorkflowSource } from '@n8n/workflow-sdk/lint/lint-workflow-source';
const issues = lintWorkflowSource(sdkSource);
if (issues.some((i) => i.code === 'SDK_CODE_AFTER_EXPORT_DEFAULT')) {
throw new Error('Statements after export default are dropped; fold all wiring into the export default chain.');
} Prevention
- Make the entire workflow a single fluent chain ending at export default.
- Never add .to()/.onTrue()/.onFalse()/.onCase() as separate top-level statements.
- Run lintWorkflowSource on generated SDK code before deploy.
When it happens
Trigger: export default workflow().to(a); a.to(b); — or any node construction, assignment, or wiring call appearing as a top-level statement after the export default line.
Common situations: Agent adds wiring as separate statements instead of chaining; developer appends nodes 'after' the export; refactor leaves orphan statements.
Related errors
- SDK_FORBIDDEN_CONSTRUCT
- SDK_REPEATED_BRANCH_WIRING
- VectorStore "${this.name}" requires an embedding model — set
- CODE_NODE_NETWORK_CALL
- CODE_NODE_FORBIDDEN_IMPORT
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/a3b75895c5fe8a19.
Report an issue: GitHub.