{"record":{"id":"7206e9fda0b97972","repo":"paperclipai/paperclip","slug":"request-runnerd-sha256-must-be-a-sha-256-digest","errorCode":null,"errorMessage":"request.runnerd.sha256 must be a SHA-256 digest","messagePattern":"request\\.runnerd\\.sha256 must be a SHA-256 digest","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/cli/eval-session-contract.ts","lineNumber":283,"sourceCode":"  }\n  if (provider !== \"aws_agentcore\" && agentCoreProfileInput !== undefined) {\n    throw new Error(\"eval-session agentCoreProfile requires provider aws_agentcore\");\n  }\n  if (input.nativeResume !== undefined) {\n    throw new Error(\n      \"eval-session nativeResume requires a retained live-session checkpoint\",\n    );\n  }\n  if (input.includeCollaborationModeInstructions === false) {\n    throw new Error(\n      \"current CapabilityLiveSessionService requires collaboration-mode instructions\",\n    );\n  }\n\n  const runnerd = object(input.runnerd, \"request.runnerd\");\n  const digest = text(runnerd.sha256, \"request.runnerd.sha256\");\n  if (!/^(?:sha256:)?[a-f0-9]{64}$/i.test(digest)) {\n    throw new Error(\"request.runnerd.sha256 must be a SHA-256 digest\");\n  }\n  const limits = object(input.limits, \"request.limits\");\n  const sessionInput = object(input.session, \"request.session\");\n  const session = sessionInput as unknown as CreateCapabilityLiveSessionInput;\n  const model = text(input.model, \"request.model\");\n  if (provider === \"claude_managed\" && model !== \"claude-sonnet-5\") {\n    throw new Error(\"Claude Managed evals require exact model claude-sonnet-5\");\n  }\n  if (\n    provider === \"aws_agentcore\" &&\n    model !== \"global.anthropic.claude-sonnet-4-6\"\n  ) {\n    throw new Error(\n      \"AWS AgentCore evals require exact model global.anthropic.claude-sonnet-4-6\",\n    );\n  }\n  if (sessionInput.provider !== undefined && sessionInput.provider !== provider) {\n    throw new Error(\"request.session.provider must match request.provider\");","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/cli/eval-session-contract.ts#L265-L301","documentation":"The runnerd section of an eval-session request identifies the runner daemon build by SHA-256 digest. parseEvalSessionRequest validates request.runnerd.sha256 with /^(?:sha256:)?[a-f0-9]{64}$/i and throws when the value is missing, not a string, or does not look like a 64-character hex digest (an optional \"sha256:\" prefix is allowed).","triggerScenarios":"Calling parseEvalSessionRequest with runnerd absent, runnerd.sha256 absent, or a sha256 value such as \"abc123\", a full Git SHA of wrong length, an uppercase non-hex string, or a digest with whitespace/newlines.","commonSituations":"Passing a Git commit hash (often not exactly 64 hex chars) instead of a sha256 content digest; emitting a truncated or placeholder digest in generated configs; a version upgrade changing the digest format; forgetting to populate runnerd when hand-writing the request.","solutions":["Populate request.runnerd.sha256 with the runner daemon's actual SHA-256 content digest (64 hex chars, optionally prefixed with \"sha256:\").","Compute it correctly, e.g. `sha256sum <runnerd-artifact> | awk '{print $1}'`, and verify length is 64.","Fix the request builder so runnerd.sha256 is never emitted empty or as a placeholder."],"exampleFix":"// before\n\"runnerd\": { \"sha256\": \"a1b2c3\" }\n// after\n\"runnerd\": { \"sha256\": \"sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08\" }","handlingStrategy":"validation","validationCode":"const SHA256_RE = /^(?:sha256:)?[a-f0-9]{64}$/i;\nif (!SHA256_RE.test(req.runnerd?.sha256 ?? \"\")) throw new Error(\"runnerd.sha256 must be a 64-hex SHA-256 digest\");","typeGuard":"function isValidRunnerdDigest(input) { return typeof input?.runnerd?.sha256 === \"string\" && /^(?:sha256:)?[a-f0-9]{64}$/i.test(input.runnerd.sha256); }","tryCatchPattern":"try { return parseEvalSessionRequest(raw); } catch (e) { if (String(e.message).includes(\"must be a SHA-256 digest\")) { throw new Error(`Bad runnerd.sha256: ${JSON.stringify(raw?.runnerd?.sha256)}; compute with sha256sum of the runnerd artifact`); } throw e; }","preventionTips":["Compute the digest from the actual runnerd artifact, never hand-write it.","Validate digests with the same regex before sending.","Distinguish Git commit SHAs from SHA-256 content digests in build tooling."],"tags":["validation","hash","request-contract"],"backgroundTag":"invalid-hash-digest","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-02T18:44:00.616Z","contentChangedAt":"2026-09-02T18:44:00.616Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}