ruvnet/ruflo · error · Error
run sourceStateId is invalid
Error message
run sourceStateId is invalid
What it means
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.
Source
Thrown at v3/@claude-flow/codex/src/harness/in-memory-run-receipt-reference.ts:25
function sha256(value: string): string {
return `sha256:${createHash('sha256').update(value).digest('hex')}`;
}
function copy<T>(value: T): T {
return structuredClone(value);
}
function timestamp(value: string, label: string): number {
const parsed = Date.parse(value);
if (!Number.isFinite(parsed)) throw new Error(`${label} must be an ISO timestamp`);
return parsed;
}
function validateRun(run: RunEvidence): void {
if (!run.executionId.trim() || !run.sessionId.trim() || !run.workloadId.trim()) {
throw new Error('run execution, session, and workload identity are required');
}
if (!DIGEST.test(run.sourceState.sourceStateId)) throw new Error('run sourceStateId is invalid');
if (!DIGEST.test(run.commandDigest) || !DIGEST.test(run.evidenceDigest)) {
throw new Error('run command and evidence digests must be canonical sha256 values');
}
if (!Number.isSafeInteger(run.exitCode)) throw new Error('run exitCode must be a safe integer');
const started = timestamp(run.startedAt, 'startedAt');
const completed = timestamp(run.completedAt, 'completedAt');
if (completed < started) throw new Error('run completedAt precedes startedAt');
if (
run.buildEvidence !== undefined
&& run.buildEvidence.sourceStateId !== run.sourceState.sourceStateId
) {
throw new Error('build evidence belongs to a different source state');
}
}
/**
* Unsigned, non-durable, content-addressed in-memory conformance ledger.
*View on GitHub (pinned to fa13ee4ad6)
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
Example fix
// before
sourceState: { sourceStateId: createHash('sha256').update(state).digest('hex') }
// after
sourceState: { sourceStateId: `sha256:${createHash('sha256').update(state).digest('hex')}` } Defensive patterns
Strategy: type-guard
Validate before calling
const SHA256_DIGEST = /^sha256:[0-9a-f]{64}$/;
function assertSourceStateId(id: string): void {
if (!SHA256_DIGEST.test(id)) {
throw new TypeError(`sourceStateId must be 'sha256:' + 64 lowercase hex chars, got: ${id}`);
}
} Type guard
function isCanonicalSha256(value: unknown): value is string {
return typeof value === 'string' && /^sha256:[0-9a-f]{64}$/.test(value);
} Try / catch
try {
ledger.recordRun(run);
} catch (error) {
if (error instanceof Error && error.message === 'run sourceStateId is invalid') {
// re-derive the snapshot and retry with its identifier
const snapshot = await captureSourceState(repoRoot);
run.sourceState = snapshot.sourceState;
ledger.recordRun(run);
} else throw error;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- run command and evidence digests must be canonical sha256 va
- ${label} must be a canonical sha256 digest
- ${label} must be an ISO timestamp
- run execution, session, and workload identity are required
- run exitCode must be a safe integer
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/19e17e7c7c54b8fe.
Report an issue: GitHub.