{"record":{"id":"acc358e330f7289c","repo":"can1357/oh-my-pi","slug":"codex-security-bundle-scan-ids-do-not-agree","errorCode":null,"errorMessage":"Codex Security bundle scan IDs do not agree","messagePattern":"Codex Security bundle scan IDs do not agree","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/security/importers/codex-security.ts","lineNumber":210,"sourceCode":"\tconst root = path.resolve(bundleDirectory);\n\tconst manifest = await readJson<CodexManifest>(path.join(root, \"scan-manifest.json\"));\n\tconst findingsDocument = await readJson<CodexFindingsDocument>(path.join(root, \"findings.json\"));\n\tconst coverageDocument = await readJson<CodexCoverageDocument>(path.join(root, \"coverage.json\"));\n\tif (manifest.documentType !== \"codex-security.scan-manifest\" || manifest.schemaVersion !== \"1.0\") {\n\t\tthrow new Error(\"Unsupported Codex Security scan manifest\");\n\t}\n\tif (findingsDocument.documentType !== \"codex-security.findings\" || findingsDocument.schemaVersion !== \"1.0\") {\n\t\tthrow new Error(\"Unsupported Codex Security findings document\");\n\t}\n\tif (coverageDocument.documentType !== \"codex-security.coverage\" || coverageDocument.schemaVersion !== \"1.0\") {\n\t\tthrow new Error(\"Unsupported Codex Security coverage document\");\n\t}\n\tif (\n\t\t!manifest.scan?.id ||\n\t\tfindingsDocument.scanId !== manifest.scan.id ||\n\t\tcoverageDocument.scanId !== manifest.scan.id\n\t) {\n\t\tthrow new Error(\"Codex Security bundle scan IDs do not agree\");\n\t}\n\tconst fixtureProvenance = await readJson<CodexFixtureProvenance>(path.join(root, \"PROVENANCE.json\")).catch(\n\t\t(): CodexFixtureProvenance => ({}),\n\t);\n\tconst scanId = options.createScanId?.() ?? createSecurityScanId();\n\tconst createdAt = options.createdAt ?? manifest.scan.startedAt ?? new Date().toISOString();\n\tconst canonicalRoot = await fs.realpath(path.resolve(options.repositoryRoot));\n\tconst producer: SecurityProducer = {\n\t\tkind: \"codex-security-bundle\",\n\t\tname: manifest.scan.producer?.name || \"codex-security\",\n\t\tvendor: \"openai\",\n\t};\n\tif (manifest.scan.producer?.version !== undefined) producer.version = manifest.scan.producer.version;\n\tif (fixtureProvenance.revision !== undefined) producer.revision = fixtureProvenance.revision;\n\tif (fixtureProvenance.pluginVersion !== undefined) producer.pluginVersion = fixtureProvenance.pluginVersion;\n\tconst upstream: SecurityUpstreamProvenance = {};\n\tif (fixtureProvenance.repository !== undefined) upstream.repository = fixtureProvenance.repository;\n\tif (fixtureProvenance.revision !== undefined) upstream.revision = fixtureProvenance.revision;","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/security/importers/codex-security.ts#L192-L228","documentation":"All three bundle documents must reference the same scan: manifest.scan.id must exist and both findings.json and coverage.json must carry the identical scanId. The importer throws when the IDs are missing or disagree, because merging documents from different scans would produce incorrect findings/coverage attribution.","triggerScenarios":"importCodexSecurityBundle where manifest.scan.id is absent, or findingsDocument.scanId !== manifest.scan.id, or coverageDocument.scanId !== manifest.scan.id — e.g. files from two different scan runs combined into one directory.","commonSituations":"Overwriting only part of a bundle directory across multiple scan runs; copying findings.json from run A with manifest/coverage from run B; generator bug omitting scan.id from the manifest.","solutions":["Re-export the full bundle from a single Codex Security scan so all three documents share one scanId","Replace the mismatched file(s) with versions from the same run as the manifest","Ensure the manifest includes a non-empty manifest.scan.id"],"exampleFix":"// before\nmanifest.scan.id = \"scan-111\", findings.scanId = \"scan-222\"\n// after: regenerate so all documents carry\n\"scanId\": \"scan-111\"","handlingStrategy":"validation","validationCode":"const [manifest, findings, coverage] = await Promise.all([\n  readJson(path.join(dir, \"scan-manifest.json\")),\n  readJson(path.join(dir, \"findings.json\")),\n  readJson(path.join(dir, \"coverage.json\")),\n]);\nconst id = manifest.scan?.id;\nif (!id || findings.scanId !== id || coverage.scanId !== id) {\n  throw new Error(`Scan ID mismatch: manifest=${id} findings=${findings.scanId} coverage=${coverage.scanId}`);\n}","typeGuard":"function scanIdsAgree(b: { manifest: { scan?: { id?: string } }; findings: { scanId: string }; coverage: { scanId: string } }): b is { manifest: { scan: { id: string } }; findings: { scanId: string }; coverage: { scanId: string } } {\n  const id = b.manifest.scan?.id;\n  return typeof id === \"string\" && id.length > 0 && b.findings.scanId === id && b.coverage.scanId === id;\n}","tryCatchPattern":"try {\n  const bundle = await importCodexSecurityBundle(dir);\n} catch (err) {\n  if (err instanceof Error && err.message === \"Codex Security bundle scan IDs do not agree\") {\n    console.error(\"Bundle documents come from different scans — re-export the full bundle from one scan run\");\n  } else throw err;\n}","preventionTips":["Always export the complete bundle (manifest + findings + coverage + provenance) from a single run","Delete the bundle directory between scan runs instead of overwriting individual files","Assert scanId consistency in CI after bundle generation","Never merge or copy documents between bundles"],"tags":["codex-security","data-integrity","import","scan-id-mismatch"],"backgroundTag":"scan-id-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}