{"record":{"id":"b6bfecbd3f148a9b","repo":"can1357/oh-my-pi","slug":"unknown-security-validation-evidence-evidenceid","errorCode":null,"errorMessage":"Unknown security validation evidence: ${evidenceId}","messagePattern":"Unknown security validation evidence: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/security/store.ts","lineNumber":405,"sourceCode":"\t\tevidence: readonly SecurityEvidence[] = [],\n\t): Promise<SecurityFinding> {\n\t\treturn withSecurityStoreWrite(this.#projectDirectory, async () => {\n\t\t\tconst bundle = await this.#getBundleUnlocked(scanId);\n\t\t\tif (!bundle) throw new Error(`Unknown security scan: ${scanId}`);\n\t\t\tconst index = bundle.findings.findIndex(finding => finding.id === findingId);\n\t\t\tif (index < 0) throw new Error(`Unknown security finding: ${findingId}`);\n\t\t\tconst finding = bundle.findings[index];\n\t\t\tconst evidenceById = new Map(finding.evidence.map(item => [item.id, item]));\n\t\t\tfor (const item of evidence) evidenceById.set(item.id, item);\n\t\t\tconst canonicalValidation: SecurityValidation = {\n\t\t\t\tstatus: validation.status,\n\t\t\t\tevidenceIds: [...new Set(validation.evidenceIds)],\n\t\t\t};\n\t\t\tif (validation.summary !== undefined) canonicalValidation.summary = validation.summary;\n\t\t\tif (validation.validatedAt !== undefined) canonicalValidation.validatedAt = validation.validatedAt;\n\t\t\tfor (const evidenceId of canonicalValidation.evidenceIds) {\n\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}`);","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/security/store.ts#L387-L423","documentation":"updateValidation() merges supplied evidence with the finding's existing evidence into an evidenceById map, then requires every id in validation.evidenceIds to exist in that map. Referencing an evidence id that is neither already on the finding nor supplied in the evidence argument throws this error — validations cannot cite evidence the store has never seen.","triggerScenarios":"Calling updateValidation(scanId, findingId, { status, evidenceIds: [...], ... }, evidence) where an evidenceIds entry has no matching evidence.id — the id was invented, typo'd, belongs to another finding, or the corresponding evidence item was omitted from the evidence argument.","commonSituations":"Recording a validation that references evidence collected for a different finding; generating evidenceIds from a report without passing the evidence objects; evidence id renamed between runs; partial evidence upload where one item failed to persist.","solutions":["Pass the full SecurityEvidence objects for any new ids in the evidence parameter of updateValidation so their ids resolve.","Only cite evidence ids already present on the finding: finding.evidence.map(e => e.id) — check before constructing validation.evidenceIds.","Fix typos in evidenceIds; ids must exactly match an existing or supplied evidence item's id.","Verify the evidence belongs to this finding; if not, update that finding instead."],"exampleFix":"// before\nawait store.updateValidation(scanId, findingId, { status: 'confirmed', evidenceIds: ['ev-9'] }); // ev-9 never stored\n// after\nawait store.updateValidation(scanId, findingId,\n  { status: 'confirmed', evidenceIds: ['ev-9'] },\n  [{ id: 'ev-9', /* ...full evidence... */ }]);","handlingStrategy":"validation","validationCode":"const finding = await store.getFinding(scanId, findingId);\nconst known = new Set(finding?.evidence.map(e => e.id) ?? []);\nfor (const e of evidence) known.add(e.id);\nconst missing = validation.evidenceIds.filter(id => !known.has(id));\nif (missing.length) throw new Error(`unsupplied evidence ids: ${missing.join(', ')}`);","typeGuard":"function allEvidenceKnown(validation: SecurityValidation, finding: SecurityFinding, extra: readonly SecurityEvidence[]): boolean {\n  const ids = new Set([...finding.evidence.map(e => e.id), ...extra.map(e => e.id)]);\n  return validation.evidenceIds.every(id => ids.has(id));\n}","tryCatchPattern":"try {\n  await store.updateValidation(scanId, findingId, validation, evidence);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Unknown security validation evidence')) {\n    // attach the missing evidence objects in the `evidence` parameter and retry\n  } else throw err;\n}","preventionTips":["Always pass full evidence objects for any evidenceIds not already on the finding.","Build evidenceIds from finding.evidence plus the evidence you supply — never from memory.","Enforce evidence id uniqueness and exact-match when generating validations.","Keep evidence collection and validation submission in one atomic step."],"tags":["security-store","not-found","evidence-reference"],"backgroundTag":"resource-not-found","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}