{"record":{"id":"19e17e7c7c54b8fe","repo":"ruvnet/ruflo","slug":"run-sourcestateid-is-invalid","errorCode":null,"errorMessage":"run sourceStateId is invalid","messagePattern":"run sourceStateId is invalid","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/codex/src/harness/in-memory-run-receipt-reference.ts","lineNumber":25,"sourceCode":"function sha256(value: string): string {\n  return `sha256:${createHash('sha256').update(value).digest('hex')}`;\n}\n\nfunction copy<T>(value: T): T {\n  return structuredClone(value);\n}\n\nfunction timestamp(value: string, label: string): number {\n  const parsed = Date.parse(value);\n  if (!Number.isFinite(parsed)) throw new Error(`${label} must be an ISO timestamp`);\n  return parsed;\n}\n\nfunction validateRun(run: RunEvidence): void {\n  if (!run.executionId.trim() || !run.sessionId.trim() || !run.workloadId.trim()) {\n    throw new Error('run execution, session, and workload identity are required');\n  }\n  if (!DIGEST.test(run.sourceState.sourceStateId)) throw new Error('run sourceStateId is invalid');\n  if (!DIGEST.test(run.commandDigest) || !DIGEST.test(run.evidenceDigest)) {\n    throw new Error('run command and evidence digests must be canonical sha256 values');\n  }\n  if (!Number.isSafeInteger(run.exitCode)) throw new Error('run exitCode must be a safe integer');\n  const started = timestamp(run.startedAt, 'startedAt');\n  const completed = timestamp(run.completedAt, 'completedAt');\n  if (completed < started) throw new Error('run completedAt precedes startedAt');\n  if (\n    run.buildEvidence !== undefined\n    && run.buildEvidence.sourceStateId !== run.sourceState.sourceStateId\n  ) {\n    throw new Error('build evidence belongs to a different source state');\n  }\n}\n\n/**\n * Unsigned, non-durable, content-addressed in-memory conformance ledger.\n *","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/codex/src/harness/in-memory-run-receipt-reference.ts#L7-L43","documentation":"run.sourceState.sourceStateId must match the DIGEST regex ^sha256:[0-9a-f]{64}$ — the canonical content-address form used throughout the harness ('sha256:' prefix plus exactly 64 lowercase hex characters). validateRun rejects bare hex, uppercase hex, sha512, base64, git SHA-1 object names, and truncated digests. The strict form keeps receipts content-addressable and cross-checkable.","triggerScenarios":"Setting sourceStateId to createHash('sha256').update(x).digest('hex') (missing prefix), 'SHA256:AB...' (wrong case), digest('base64'), a 40-char git commit hash, or a UUID.","commonSituations":"Hashing by hand instead of reusing the sourceStateId produced by the repository source-state snapshot API; copy-pasting git commit SHAs as 'source state IDs'; mixing digest formats when porting evidence from another tool.","solutions":["Take sourceStateId from the repository source-state snapshot the run actually executed against","If hashing manually, format exactly: `sha256:${createHash('sha256').update(value).digest('hex')}`","Pre-validate with /^sha256:[0-9a-f]{64}$/ before recordRun"],"exampleFix":"// before\nsourceState: { sourceStateId: createHash('sha256').update(state).digest('hex') }\n\n// after\nsourceState: { sourceStateId: `sha256:${createHash('sha256').update(state).digest('hex')}` }","handlingStrategy":"type-guard","validationCode":"const SHA256_DIGEST = /^sha256:[0-9a-f]{64}$/;\nfunction assertSourceStateId(id: string): void {\n  if (!SHA256_DIGEST.test(id)) {\n    throw new TypeError(`sourceStateId must be 'sha256:' + 64 lowercase hex chars, got: ${id}`);\n  }\n}","typeGuard":"function isCanonicalSha256(value: unknown): value is string {\n  return typeof value === 'string' && /^sha256:[0-9a-f]{64}$/.test(value);\n}","tryCatchPattern":"try {\n  ledger.recordRun(run);\n} catch (error) {\n  if (error instanceof Error && error.message === 'run sourceStateId is invalid') {\n    // re-derive the snapshot and retry with its identifier\n    const snapshot = await captureSourceState(repoRoot);\n    run.sourceState = snapshot.sourceState;\n    ledger.recordRun(run);\n  } else throw error;\n}","preventionTips":["Always take sourceStateId from the source-state snapshot API rather than hashing by hand","Centralize digest creation in one helper that emits 'sha256:' + lowercase hex","Unit-test the digest helper against the canonical regex"],"tags":["validation","digest","sha256","run-receipt"],"backgroundTag":"invalid-content-digest","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}