{"record":{"id":"81cce7a8aa76542a","repo":"coleam00/Archon","slug":"node-node-id-cannot-resume-sourcenodeid-81cce7","errorCode":null,"errorMessage":"Node '${node.id}' cannot resume '${sourceNodeId}': resolved provider '${provider}' does not support immutable session forks.","messagePattern":"Node '(.+?)' cannot resume '(.+?)': resolved provider '(.+?)' does not support immutable session forks\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/workflows/src/dag-executor.ts","lineNumber":10444,"sourceCode":"              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\n            // SAME provider — on a provider change the node starts fresh instead of failing\n            // (Claude) or silently cold-falling-back (Codex) on a foreign session id.\n            //\n            // A composed workflow's ENTRY node is the third fresh case (#1764). Standalone,\n            // that node runs first and has no cursor to inherit; composed, it would silently\n            // pick up the session of whatever the parent ran before it — the same file","sourceCodeStart":10426,"sourceCodeEnd":10462,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/workflows/src/dag-executor.ts#L10426-L10462","documentation":"Beyond provider identity, session resume requires the resolved provider to declare both sessionResume and immutable session forks (sessionFork === true) in getCapabilities(). If the resolved provider lacks these, the executor cannot safely fork the source session and throws before starting the node.","triggerScenarios":"Node B resumes from A's session while B's resolved provider's getCapabilities() returns sessionResume:false or sessionFork !== true (e.g. a provider that supports resume but not forks, or a mock provider with default caps).","commonSituations":"Switching to a provider that implements sessions differently (no fork semantics); using a custom or mock provider in tests without declaring sessionFork; a provider capability regression after an SDK upgrade.","solutions":["Use a provider whose capabilities include sessionResume and sessionFork: true (e.g. claude) for the resuming node","Remove the named resume (resume_from) so the node starts its own fresh session","For custom/mock providers, implement getCapabilities() returning { sessionResume: true, sessionFork: true } and real fork behavior","Check the provider registry/capability docs to confirm fork support before wiring session-resume nodes"],"exampleFix":"// before: provider without fork support\ngetCapabilities() { return { sessionResume: true } }\n// after\ngetCapabilities() { return { sessionResume: true, sessionFork: true } }","handlingStrategy":"validation","validationCode":"const caps = ctx.deps.getAgentProvider(provider).getCapabilities();\nif (!caps.sessionResume || caps.sessionFork !== true) throw new Error(`${provider} cannot fork sessions; drop resume_from or switch provider`);","typeGuard":"function supportsSessionFork(caps: ProviderCapabilities): boolean {\n  return caps.sessionResume === true && caps.sessionFork === true;\n}","tryCatchPattern":"try {\n  await engine.resumeFrom(runId, nodeId, sourceNodeId);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('does not support immutable session forks')) {\n    // start a fresh session instead of forking\n  } else throw err;\n}","preventionTips":["Check getCapabilities() before authoring resume_from wiring","For mock providers, declare sessionResume/sessionFork and implement fork behavior","Prefer providers with documented immutable-fork semantics (e.g. claude) for resume chains"],"tags":["session-resume","provider-capabilities","session-fork"],"backgroundTag":"unsupported-provider-capability","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}