{"record":{"id":"342fa956f11c86d0","repo":"JuliusBrussee/caveman","slug":"hook-payload-too-large","errorCode":null,"errorMessage":"hook payload too large","messagePattern":"hook payload too large","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/native-hook-fast.ts","lineNumber":100,"sourceCode":"  if (explicit !== undefined && explicit !== \"\") return explicit === \"safe\" || explicit === \"max\" || explicit === \"record\" ? explicit : \"record\";\n  const mode = configuredMode();\n  return mode === \"record\" ? \"record\" : mode === \"pixel\" ? \"max\" : \"safe\";\n}\n\nfunction profile(): NativeProfile {\n  const explicit = process.env.CAVEMAN_NATIVE_PROFILE?.trim().toLowerCase() as NativeProfile | undefined;\n  if (explicit) return PROFILES.has(explicit) ? explicit : \"record-only\";\n  const mode = policyMode();\n  return mode === \"record\" ? \"record-only\" : mode === \"max\" ? \"full-max\" : \"full-safe\";\n}\n\nasync function stdin(): Promise<Buffer> {\n  const chunks: Buffer[] = [];\n  let bytes = 0;\n  for await (const chunk of process.stdin) {\n    const value = Buffer.from(chunk);\n    bytes += value.length;\n    if (bytes > 2 * 1024 * 1024) throw new Error(\"hook payload too large\");\n    chunks.push(value);\n  }\n  return Buffer.concat(chunks);\n}\n\nfunction bounded(value: unknown, max = 160): string | undefined {\n  if (typeof value !== \"string\") return undefined;\n  const clean = value.replace(/[\\r\\n\\0]/g, \" \").trim();\n  return clean ? clean.slice(0, max) : undefined;\n}\n\nfunction digestObject(value: unknown): { bytes: number; sha256: string } | undefined {\n  if (value && typeof value === \"object\" && !Array.isArray(value)) {\n    const candidate = value as Record<string, unknown>;\n    if (typeof candidate.bytes === \"number\" && candidate.bytes >= 0 && typeof candidate.sha256 === \"string\" && /^sha256:[0-9a-f]{6,64}$/i.test(candidate.sha256)) {\n      return { bytes: Math.floor(candidate.bytes), sha256: candidate.sha256.toLowerCase() };\n    }\n  }","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/cli/src/native-hook-fast.ts#L82-L118","documentation":"The native fast hook reads the agent's hook payload (tool-call JSON) from stdin with a hard 2 MiB cap to keep the hot path cheap and avoid unbounded memory. When accumulated chunk bytes exceed 2*1024*1024 the read loop aborts with this error. It is a deliberate resource guard, not a parsing failure.","triggerScenarios":"An agent emits a hook event (e.g. PreToolUse/PostToolUse JSON) whose serialized payload exceeds 2 MiB — typically huge file writes, enormous command strings, or massive tool outputs embedded in the event.","commonSituations":"Write/Edit tools with multi-megabyte file contents, pasted base64 blobs, agents streaming giant diffs through hooks, or a misconfigured hook that forwards whole conversation transcripts.","solutions":["Reduce the payload: have the tool input reference files by path instead of inlining megabytes of content.","Filter hook events: configure the agent's hook matcher so only the event names you need are delivered to the native hook.","If you control the producer, chunk or truncate large fields before they reach the hook process.","Do not attempt to raise the cap by patching; it is compiled into the guard — restructure the payload instead."],"exampleFix":"// before: tool input inlines a 5 MiB string\n{ \"tool_input\": { \"content\": \"<5 MiB of text>\" } }\n// after: reference by path\n{ \"tool_input\": { \"path\": \"large-file.txt\" } }","handlingStrategy":"validation","validationCode":"// Producer side: keep hook events small before they reach the native hook\nfunction assertPayloadSize(json: unknown): void {\n  const bytes = Buffer.byteLength(JSON.stringify(json) ?? \"\");\n  if (bytes > 2 * 1024 * 1024) {\n    throw new Error(`hook payload ${bytes}B exceeds 2 MiB; trim tool_input content`);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const payload = await stdin();\n} catch (e) {\n  if (e instanceof Error && e.message === \"hook payload too large\") {\n    process.exit(0); // treat oversized event as no-op: never fail the agent's tool call\n  }\n  throw e;\n}","preventionTips":["Reference large content by path in tool inputs instead of inlining it.","Scope hook matchers to the specific events the hook needs (avoid forwarding everything).","Treat the 2 MiB cap as a contract when designing custom tools for agents."],"tags":["limits","hooks","stdin","payload-size"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}