coleam00/Archon · error · Error

Node '${node.id}' cannot resume '${sourceNodeId}': resolved

Error message

Node '${node.id}' cannot resume '${sourceNodeId}': resolved provider '${provider}' does not support immutable session forks.

What it means

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.

Source

Thrown at packages/workflows/src/dag-executor.ts:10444

              const sourceHandle = ctx.nodeSessionHandles?.get(sourceNodeId);
              if (sourceHandle === undefined) {
                throw new Error(
                  `Node '${node.id}' cannot resume '${sourceNodeId}': the completed source has no available provider session.`
                );
              }
              if (sourceHandle.sessionId.trim() === '') {
                throw new Error(
                  `Node '${node.id}' cannot resume '${sourceNodeId}': the completed source has no available provider session.`
                );
              }
              if (sourceHandle.provider !== provider) {
                throw new Error(
                  `Node '${node.id}' cannot resume '${sourceNodeId}': source provider '${sourceHandle.provider}' does not match resolved provider '${provider}'.`
                );
              }
              const caps = ctx.deps.getAgentProvider(provider).getCapabilities();
              if (!caps.sessionResume || caps.sessionFork !== true) {
                throw new Error(
                  `Node '${node.id}' cannot resume '${sourceNodeId}': resolved provider '${provider}' does not support immutable session forks.`
                );
              }
              resumeSessionId = sourceHandle.sessionId;
            }

            // Legacy scalar/default selection — parallel or context:fresh → always fresh.
            // Parallel layers always get fresh sessions; explicit 'fresh' context also forces it.
            // 'shared' forces continuation. Default: fresh for parallel, inherited for sequential.
            // isFreshSequential controls in-run threading (lastSequentialSession).
            // Cross-provider guard (#1992): a session id can only be resumed by the provider
            // that created it, so the cursor is threaded only into nodes that resolve to the
            // SAME provider — on a provider change the node starts fresh instead of failing
            // (Claude) or silently cold-falling-back (Codex) on a foreign session id.
            //
            // A composed workflow's ENTRY node is the third fresh case (#1764). Standalone,
            // that node runs first and has no cursor to inherit; composed, it would silently
            // pick up the session of whatever the parent ran before it — the same file

View on GitHub (pinned to 0773b97458)

Solutions

  1. Use a provider whose capabilities include sessionResume and sessionFork: true (e.g. claude) for the resuming node
  2. Remove the named resume (resume_from) so the node starts its own fresh session
  3. For custom/mock providers, implement getCapabilities() returning { sessionResume: true, sessionFork: true } and real fork behavior
  4. Check the provider registry/capability docs to confirm fork support before wiring session-resume nodes

Example fix

// before: provider without fork support
getCapabilities() { return { sessionResume: true } }
// after
getCapabilities() { return { sessionResume: true, sessionFork: true } }
Defensive patterns

Strategy: validation

Validate before calling

const caps = ctx.deps.getAgentProvider(provider).getCapabilities();
if (!caps.sessionResume || caps.sessionFork !== true) throw new Error(`${provider} cannot fork sessions; drop resume_from or switch provider`);

Type guard

function supportsSessionFork(caps: ProviderCapabilities): boolean {
  return caps.sessionResume === true && caps.sessionFork === true;
}

Try / catch

try {
  await engine.resumeFrom(runId, nodeId, sourceNodeId);
} catch (err) {
  if (err instanceof Error && err.message.includes('does not support immutable session forks')) {
    // start a fresh session instead of forking
  } else throw err;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/81cce7a8aa76542a. Report an issue: GitHub.