{"record":{"id":"3aad056e596d5cdd","repo":"JuliusBrussee/caveman","slug":"cave-recovery-handle-out-of-scope","errorCode":null,"errorMessage":"cave_recovery_handle_out_of_scope","messagePattern":"cave_recovery_handle_out_of_scope","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/agent/src/runtime.ts","lineNumber":3041,"sourceCode":"}\n\nfunction recoveryTool(\n  handles: ReadonlySet<string>,\n  engineBin: string | undefined,\n  trace?: MutableTransformTrace[],\n): AgentTool<TSchema> {\n  return {\n    name: \"cave_retrieve\",\n    label: \"cave_retrieve\",\n    description: \"Recover exact content omitted by an active Caveman transform. Handles are scoped to this run.\",\n    parameters: Type.Object({\n      recovery_handle: Type.String(),\n      query: Type.Optional(Type.String()),\n    }),\n    executionMode: \"parallel\",\n    async execute(_toolCallId, params) {\n      const input = params as { recovery_handle: string; query?: string };\n      if (!handles.has(input.recovery_handle)) throw new Error(\"cave_recovery_handle_out_of_scope\");\n      const value = await engineRetrieve(input.recovery_handle, input.query, engineBin);\n      for (const item of trace ?? []) {\n        if (item.recoveryHandle === input.recovery_handle) item.recoveryUsed = true;\n      }\n      return {\n        content: [{ type: \"text\", text: new TextDecoder().decode(value) }],\n        details: { recovery: input.query ? \"query\" : \"exact\" },\n      };\n    },\n  };\n}\n\nfunction toolSchemaSearchTool(\n  handles: ReadonlySet<string>,\n  engineBin: string | undefined,\n  trace?: MutableTransformTrace[],\n): AgentTool<TSchema> {\n  return {","sourceCodeStart":3023,"sourceCodeEnd":3059,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/runtime.ts#L3023-L3059","documentation":"Thrown by the cave_retrieve tool when the model supplies a recovery_handle that is not in this run's handle set. Handles are deliberately scoped to a single run (see the tool description) so omitted content cannot be pulled back with stale or foreign handles.","triggerScenarios":"The model calls cave_retrieve with a handle from a previous run, a hallucinated/invented handle string, or a handle from a different segment than those registered for this run.","commonSituations":"Conversation replay/resume where the model remembers old handles; models hallucinating handle strings; prompts containing example handles the model copies.","solutions":["No code fix needed for correctness — the guard is the fix; but reduce occurrences: avoid replaying prior-run transcripts that contain stale handles into the model's context","If resuming conversations, start a fresh run so the model never sees handles that expired","Sanitize few-shot examples so they don't include realistic-looking handle strings the model will imitate"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isHandleOutOfScope(e: unknown): e is Error {\n  return e instanceof Error && e.message === \"cave_recovery_handle_out_of_scope\";\n}","tryCatchPattern":"// Inside tool-result handling:\ntry {\n  const out = await caveRetrieve.execute(callId, { recovery_handle: h });\n} catch (e) {\n  if (isHandleOutOfScope(e)) {\n    // return a tool error telling the model the handle expired for this run\n  } else throw e;\n}","preventionTips":["Start a fresh run when resuming conversations so stale handles never enter context","Strip previous-run tool transcripts (with handles) from replayed history","Sanitize few-shot examples so they contain no realistic handle strings"],"tags":["recovery","tool","scope","model-behavior"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}