{"record":{"id":"da643b576bab252e","repo":"can1357/oh-my-pi","slug":"unknown-security-scan-beforescanid","errorCode":null,"errorMessage":"Unknown security scan: ${beforeScanId}","messagePattern":"Unknown security scan: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/security/store.ts","lineNumber":422,"sourceCode":"\t\t\t\tif (!evidenceById.has(evidenceId)) {\n\t\t\t\t\tthrow new Error(`Unknown security validation evidence: ${evidenceId}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbundle.findings[index] = parseSecurityFinding({\n\t\t\t\t...finding,\n\t\t\t\tevidence: [...evidenceById.values()],\n\t\t\t\tvalidation: canonicalValidation,\n\t\t\t});\n\t\t\tif (bundle.sarif !== undefined) bundle.sarif = exportSecurityBundleToSarif(bundle);\n\t\t\tawait this.#putBundleUnlocked(bundle);\n\t\t\treturn bundle.findings[index];\n\t\t});\n\t}\n\n\tasync compare(beforeScanId: string, afterScanId: string): Promise<SecurityComparisonReport> {\n\t\tconst before = await this.getBundle(beforeScanId);\n\t\tconst after = await this.getBundle(afterScanId);\n\t\tif (!before) throw new Error(`Unknown security scan: ${beforeScanId}`);\n\t\tif (!after) throw new Error(`Unknown security scan: ${afterScanId}`);\n\t\treturn compareSecurityLineage(before, after);\n\t}\n\n\tasync storeDigest(): Promise<string> {\n\t\tconst index = await this.#readIndex();\n\t\treturn Bun.SHA256.hash(JSON.stringify(index), \"hex\");\n\t}\n}\n","sourceCodeStart":404,"sourceCodeEnd":432,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/security/store.ts#L404-L432","documentation":"compare() loads both scan bundles via getBundle() (which returns null for unknown scans) and throws this error when the beforeScanId does not resolve. The message names beforeScanId specifically; a companion throw covers afterScanId. Comparison requires both endpoints to exist in this store.","triggerScenarios":"Calling compare(beforeScanId, afterScanId) — also reached via matchesAt() — where the before scan has no stored bundle: typo'd id, scan from another repo's store, deleted scans directory, or ids passed in swapped/wrong order in caller code.","commonSituations":"Building lineage/CI gates where the baseline scan was pruned; running comparison from a different checkout whose project directory differs; hardcoding baseline ids in pipeline config that another environment doesn't have; argument order confusion.","solutions":["Check both scans first: const before = await store.getBundle(beforeScanId) and handle null with a clear message before calling compare().","List available scans with store.listScans() and pick existing ids (typically the two most recent).","Open the SecurityStore for the same repositoryRoot/stateRoot that produced the scans.","If the baseline was deleted, re-run it via putBundle() before comparing."],"exampleFix":"// before\nconst report = await store.compare(cfg.baselineScanId, cfg.currentScanId); // baseline pruned\n// after\nconst baseline = await store.getBundle(cfg.baselineScanId) ?? (await store.listScans()).at(-1);\nif (!baseline) throw new Error('no baseline scan available; run a scan first');\nconst report = await store.compare(baseline.scan.id, cfg.currentScanId);","handlingStrategy":"fallback","validationCode":"const before = await store.getBundle(beforeScanId);\nconst after = await store.getBundle(afterScanId);\nif (!before || !after) throw new Error(`scans missing: ${!before ? beforeScanId : afterScanId}`);","typeGuard":"function bothBundles(b: SecurityScanBundle | null, a: SecurityScanBundle | null): b is SecurityScanBundle {\n  return b !== null && a !== null;\n}","tryCatchPattern":"try {\n  const report = await store.compare(beforeScanId, afterScanId);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Unknown security scan')) {\n    const scans = await store.listScans();\n    if (scans.length >= 2) return store.compare(scans[1].id, scans[0].id); // fall back to latest pair\n  } else throw err;\n}","preventionTips":["Pre-validate both endpoints with getBundle() before compare().","Resolve baseline ids dynamically via listScans() instead of hardcoding them in CI config.","Run compare against the store opened from the same repo root that produced the scans.","Make scan retention policies keep the baselines referenced by comparisons."],"tags":["security-store","not-found","scan-id","comparison"],"backgroundTag":"resource-not-found","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}