{"record":{"id":"a2cb6974e90a5787","repo":"coleam00/Archon","slug":"node-node-id-exceeded-cost-cap-cap-undef","errorCode":null,"errorMessage":"Node '${node.id}' exceeded cost cap${cap !== undefined ? ` of $${cap.toFixed(2)}` : ''}.","messagePattern":"Node '(.+?)' exceeded cost cap(.+?)` : ''\\}\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/workflows/src/dag-executor.ts","lineNumber":2627,"sourceCode":"          }\n        }\n        if (msg.stopReason !== undefined) nodeStopReason = msg.stopReason;\n        if (msg.numTurns !== undefined) nodeNumTurns = msg.numTurns;\n        // Assigned UNCONDITIONALLY. A guarded assignment cannot CLEAR a stale value:\n        // Pi/Copilot reask loops yield several result chunks, and Pi omits resolvedModel\n        // when its later assistant message has no responseModel — so an earlier attempt's\n        // model would be persisted as the final attempt's answer. Fabricated attribution\n        // is the exact defect #2314 exists to prevent; absence must stay absence.\n        nodeResolvedModel = msg.resolvedModel;\n        if (msg.structuredOutput !== undefined) structuredOutput = msg.structuredOutput;\n        // Fail the node if the SDK reports a cost cap exceeded error\n        if (msg.isError && msg.errorSubtype === 'error_max_budget_usd') {\n          const cap = nodeOptions?.maxBudgetUsd;\n          getLog().warn(\n            { nodeId: node.id, maxBudgetUsd: cap, durationMs: Date.now() - nodeStartTime },\n            'dag.node_budget_cap_exceeded'\n          );\n          throw new Error(\n            `Node '${node.id}' exceeded cost cap${cap !== undefined ? ` of $${cap.toFixed(2)}` : ''}.`\n          );\n        }\n        // Fail loudly on any other SDK error result. Previously we broke out of\n        // the stream silently, producing empty/partial output without signaling\n        // failure — which let failed iterations masquerade as successes.\n        // Exception: errorSubtype === 'success' is the Claude SDK's marker for a\n        // clean stop_sequence termination. The Claude provider already filters\n        // this out, but the guard here keeps a third-party IAgentProvider that\n        // forwards the SDK pair raw from producing a \"SDK returned success\"\n        // false failure.\n        if (msg.isError && msg.errorSubtype !== 'success') {\n          const subtype = msg.errorSubtype ?? 'unknown';\n          const errorsDetail = msg.errors?.length ? ` — ${msg.errors.join('; ')}` : '';\n          getLog().error(\n            {\n              nodeId: node.id,\n              errorSubtype: subtype,","sourceCodeStart":2609,"sourceCodeEnd":2645,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/workflows/src/dag-executor.ts#L2609-L2645","documentation":"Thrown when the SDK reports an error_max_budget_usd result for an agent node, meaning the node consumed more than its configured maxBudgetUsd cost cap. The executor fails the node loudly (after logging dag.node_budget_cap_exceeded) instead of treating the capped stop as success, because partial output would masquerade as a completed run.","triggerScenarios":"A node's options set maxBudgetUsd and the underlying model SDK ended the turn with an error_max_budget_usd result because accumulated spend crossed the cap.","commonSituations":"Long agent loops burning tokens on a hard task; an unexpectedly expensive model or large context; a cap set too low for the task's realistic token usage.","solutions":["Raise maxBudgetUsd on the node options to a realistic ceiling.","Reduce token consumption: trim prompts/context, use a cheaper model for the node, or cap iterations.","Split the work across nodes so each has its own budget.","If the cap was intentional, treat this failure as the designed safety stop and handle the node failure downstream."],"exampleFix":"// before\noptions: { maxBudgetUsd: 0.5 }\n// after\noptions: { maxBudgetUsd: 5.0 }","handlingStrategy":"try-catch","validationCode":"// pre-check: estimate expected spend vs cap\nif (nodeOptions.maxBudgetUsd !== undefined && nodeOptions.maxBudgetUsd < estimatedMinSpendUsd) {\n  console.warn(`maxBudgetUsd ${nodeOptions.maxBudgetUsd} likely too low (est. min $${estimatedMinSpendUsd})`);\n}","typeGuard":"function hasBudgetCap(o: unknown): o is { maxBudgetUsd: number } {\n  return typeof o === 'object' && o !== null && 'maxBudgetUsd' in o && typeof (o as any).maxBudgetUsd === 'number';\n}","tryCatchPattern":"try {\n  await runNode(node);\n} catch (e) {\n  if (/exceeded cost cap/.test(String(e))) {\n    log.warn('budget cap hit — raise maxBudgetUsd or reduce scope');\n  } else throw e;\n}","preventionTips":["Set maxBudgetUsd from measured spend of similar past runs, not guesses.","Monitor dag.node_budget_cap_exceeded warnings to tune caps.","Use cheaper models or smaller contexts for high-volume nodes."],"tags":["budget","cost-limit","agent"],"backgroundTag":"budget-cap-exceeded","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}