{"record":{"id":"c5d6d7b9bb68969b","repo":"ruvnet/ruflo","slug":"invalid-receipt-id","errorCode":null,"errorMessage":"invalid receipt ID","messagePattern":"invalid receipt ID","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/services/flywheel-transaction.ts","lineNumber":265,"sourceCode":"        try { fs.unlinkSync(lock); } catch { /* lock already gone */ }\n      }\n    } catch (error) {\n      if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;\n      try {\n        const stat = fs.lstatSync(lock);\n        if (Date.now() - stat.mtimeMs > LOCK_STALE_MS) {\n          fs.unlinkSync(lock);\n          continue;\n        }\n      } catch { /* raced with owner */ }\n      if (Date.now() >= deadline) throw new Error('timed out acquiring flywheel transaction lock');\n      await delay(5);\n    }\n  }\n}\n\nfunction validateReceiptId(receiptId: string): void {\n  if (!/^sha256:[a-f0-9]{64}$/.test(receiptId)) throw new Error('invalid receipt ID');\n}\n\nfunction receiptPath(root: string, receiptId: string): string {\n  validateReceiptId(receiptId);\n  return path.join(receiptDir(root), `${receiptId.slice('sha256:'.length)}.json`);\n}\n\nexport function readFlywheelReceipt(root: string, receiptId: string): FlywheelEvaluationReceipt | null {\n  try {\n    const file = receiptPath(root, receiptId);\n    assertSafeFile(file);\n    return JSON.parse(fs.readFileSync(file, 'utf8')) as FlywheelEvaluationReceipt;\n  } catch {\n    return null;\n  }\n}\n\n/**","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/services/flywheel-transaction.ts#L247-L283","documentation":"Thrown by validateReceiptId() when a receipt ID does not match the strict regex ^sha256:[a-f0-9]{64}$. Receipt IDs are content hashes of the canonicalized payload (sha256Ref output). The regex is also a path-traversal guard: it forbids slashes, dots, and uppercase, so a crafted ID cannot escape the receipts/ directory.","triggerScenarios":"Calling readFlywheelReceipt(root, receiptId) or any path that routes through receiptPath() with an ID like 'sha256:ABC...', 'abc123', 'sha256:short', '../evil', or a missing 'sha256:' prefix.","commonSituations":"Passing a raw hex digest without the 'sha256:' prefix; using uppercase hex (must be lowercase); truncating the hash; a CLI/UX bug that passed user input unvalidated; a path-injection attempt.","solutions":["Always use the receiptId returned by createFlywheelReceipt() (payload.receiptId).","If constructing manually, format as `sha256:${lowercaseHex64}`.","Validate with the same regex before calling read APIs: /^sha256:[a-f0-9]{64}$/.test(id)."],"exampleFix":"// before\nreadFlywheelReceipt(root, '6096e48ef8f2182e...'); // missing prefix\n// after\nreadFlywheelReceipt(root, `sha256:6096e48ef8f2182e...`); // or use receipt.payload.receiptId","handlingStrategy":"validation","validationCode":"const RECEIPT_ID_RE = /^sha256:[a-f0-9]{64}$/;\nfunction assertValidReceiptId(id: string): void {\n  if (!RECEIPT_ID_RE.test(id)) {\n    throw new Error(`receipt ID must be sha256:<64 lowercase hex>, got: ${id}`);\n  }\n}\nassertValidReceiptId(receiptId);","typeGuard":"const isValidReceiptId = (x: unknown): x is string => typeof x === 'string' && /^sha256:[a-f0-9]{64}$/.test(x);","tryCatchPattern":"try {\n  readFlywheelReceipt(root, id);\n} catch (e) {\n  if (e instanceof Error && e.message === 'invalid receipt ID') {\n    throw new Error(`rejecting untrusted receipt ID input: ${id}`);\n  }\n  throw e;\n}","preventionTips":["Always source receipt IDs from createFlywheelReceipt output (payload.receiptId).","Never accept raw user input as a receipt ID without regex validation.","Lowercase hex before formatting if you construct IDs manually."],"tags":["validation","security","path-traversal","receipt"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}