{"record":{"id":"0bf9e1677cb2c3f9","repo":"coleam00/Archon","slug":"node-node-id-cannot-resume-sourcenodeid-0bf9e1","errorCode":null,"errorMessage":"Node '${node.id}' cannot resume '${sourceNodeId}': source provider '${sourceHandle.provider}' does not match resolved provider '${provider}'.","messagePattern":"Node '(.+?)' cannot resume '(.+?)': source provider '(.+?)' does not match resolved provider '(.+?)'\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/workflows/src/dag-executor.ts","lineNumber":10438,"sourceCode":"              ? node.context.resume\n              : undefined;\n            const hasNamedSessionResume = namedResumeSourceNodeId !== undefined;\n            let resumeSessionId: string | undefined;\n            if (hasNamedSessionResume) {\n              const sourceNodeId = namedResumeSourceNodeId;\n              const sourceHandle = ctx.nodeSessionHandles?.get(sourceNodeId);\n              if (sourceHandle === undefined) {\n                throw new Error(\n                  `Node '${node.id}' cannot resume '${sourceNodeId}': the completed source has no available provider session.`\n                );\n              }\n              if (sourceHandle.sessionId.trim() === '') {\n                throw new Error(\n                  `Node '${node.id}' cannot resume '${sourceNodeId}': the completed source has no available provider session.`\n                );\n              }\n              if (sourceHandle.provider !== provider) {\n                throw new Error(\n                  `Node '${node.id}' cannot resume '${sourceNodeId}': source provider '${sourceHandle.provider}' does not match resolved provider '${provider}'.`\n                );\n              }\n              const caps = ctx.deps.getAgentProvider(provider).getCapabilities();\n              if (!caps.sessionResume || caps.sessionFork !== true) {\n                throw new Error(\n                  `Node '${node.id}' cannot resume '${sourceNodeId}': resolved provider '${provider}' does not support immutable session forks.`\n                );\n              }\n              resumeSessionId = sourceHandle.sessionId;\n            }\n\n            // Legacy scalar/default selection — parallel or context:fresh → always fresh.\n            // Parallel layers always get fresh sessions; explicit 'fresh' context also forces it.\n            // 'shared' forces continuation. Default: fresh for parallel, inherited for sequential.\n            // isFreshSequential controls in-run threading (lastSequentialSession).\n            // Cross-provider guard (#1992): a session id can only be resumed by the provider\n            // that created it, so the cursor is threaded only into nodes that resolve to the","sourceCodeStart":10420,"sourceCodeEnd":10456,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/workflows/src/dag-executor.ts#L10420-L10456","documentation":"When resuming from a named source node, the executor checks that the source node's recorded provider matches the provider resolved for the resuming node. Session handles are provider-specific; forking a session recorded by, say, claude under a codex-resolved node would hand an incompatible session id to the new provider, so it throws.","triggerScenarios":"Node B has resume_from: A, but B's provider (explicit node provider, AI profile, or .archon/config.yaml default) resolves to a different provider than the one A actually ran with and recorded in its session handle.","commonSituations":"Changing the global default provider in .archon/config.yaml after the source node's run; setting an aiProfile that overrides the provider for the resuming node only; renaming provider aliases so resolution no longer matches the source's provider.","solutions":["Pin the same explicit provider on both the source and resuming nodes so resolution matches","Remove the provider override (aiProfile or config default) that makes the resuming node resolve differently","Re-run the whole workflow under the new provider so the source handle is recreated with the matching provider","Update the provider alias/registry so the resolved provider id equals the recorded one"],"exampleFix":"// before: only the resuming node pins a provider\n- id: research\n  persist_session: true\n- id: continue\n  resume_from: research\n  provider: codex\n// after: both nodes use the same provider\n- id: research\n  persist_session: true\n  provider: claude\n- id: continue\n  resume_from: research\n  provider: claude","handlingStrategy":"validation","validationCode":"const src = ctx.nodeSessionHandles?.get(sourceNodeId);\nconst resolved = resolveProvider(node, aiProfile, config);\nif (src && src.provider !== resolved) throw new Error(`resume provider mismatch: ${src.provider} vs ${resolved}`);","typeGuard":"function providersMatch(handle: NodeSessionHandle | undefined, provider: string): boolean {\n  return handle !== undefined && handle.provider === provider;\n}","tryCatchPattern":"try {\n  await engine.resumeFrom(runId, nodeId, sourceNodeId);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('does not match resolved provider')) {\n    // pin provider on both nodes or re-run under the new provider\n  } else throw err;\n}","preventionTips":["Pin the same explicit provider on source and resuming nodes","Avoid changing config default providers between the source run and the resume","Log the resolved provider per node during development"],"tags":["session-resume","provider-mismatch","workflow-config"],"backgroundTag":"session-provider-mismatch","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}