{"record":{"id":"812d85215745f6c9","repo":"can1357/oh-my-pi","slug":"security-scan-scanid-has-no-report","errorCode":null,"errorMessage":"Security scan ${scanId} has no report","messagePattern":"Security scan (.+?) has no report","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/coding-agent/src/internal-urls/security-protocol.ts","lineNumber":211,"sourceCode":"\t\t\t\tconst findingId = parts[3];\n\t\t\t\tconst finding = await store.getFinding(scanId, findingId);\n\t\t\t\tif (!finding) throw new Error(`Unknown security finding: ${findingId}`);\n\t\t\t\treturn createSecurityResource({\n\t\t\t\t\turl: `security://scans/${scanId}/findings/${findingId}`,\n\t\t\t\t\tcontent: formatFinding(finding),\n\t\t\t\t\tcontentType: \"text/markdown\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tcase \"coverage\":\n\t\t\t\tif (parts.length !== 3) throw new Error(`Unknown security resource: security://${parts.join(\"/\")}`);\n\t\t\t\treturn createSecurityResource({\n\t\t\t\t\turl: `security://scans/${scanId}/coverage`,\n\t\t\t\t\tcontent: `${JSON.stringify(bundle.scan.coverage, null, 2)}\\n`,\n\t\t\t\t\tcontentType: \"application/json\",\n\t\t\t\t});\n\t\t\tcase \"report\":\n\t\t\t\tif (parts.length !== 3) throw new Error(`Unknown security resource: security://${parts.join(\"/\")}`);\n\t\t\t\tif (bundle.report === undefined) throw new Error(`Security scan ${scanId} has no report`);\n\t\t\t\treturn createSecurityResource({\n\t\t\t\t\turl: `security://scans/${scanId}/report`,\n\t\t\t\t\tcontent: bundle.report,\n\t\t\t\t\tcontentType: \"text/markdown\",\n\t\t\t\t});\n\t\t\tcase \"sarif\":\n\t\t\t\tif (parts.length !== 3) throw new Error(`Unknown security resource: security://${parts.join(\"/\")}`);\n\t\t\t\tif (bundle.sarif === undefined) throw new Error(`Security scan ${scanId} has no SARIF export`);\n\t\t\t\treturn createSecurityResource({\n\t\t\t\t\turl: `security://scans/${scanId}/sarif`,\n\t\t\t\t\tcontent: `${JSON.stringify(bundle.sarif, null, 2)}\\n`,\n\t\t\t\t\tcontentType: \"application/json\",\n\t\t\t\t});\n\t\t\tcase \"provenance\":\n\t\t\t\tif (parts.length !== 3) throw new Error(`Unknown security resource: security://${parts.join(\"/\")}`);\n\t\t\t\treturn createSecurityResource({\n\t\t\t\t\turl: `security://scans/${scanId}/provenance`,\n\t\t\t\t\tcontent: `${JSON.stringify(redactPrivateSecurityMetadata(bundle.scan.provenance), null, 2)}\\n`,","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/security-protocol.ts#L193-L229","documentation":"When security://scans/<id>/report is resolved with a valid shape, the handler checks bundle.report; if the scan bundle has no stored report (report === undefined), it throws \"Security scan <scanId> has no report\". A scan can complete without a human-readable report being generated (report generation is a separate artifact from findings/coverage/sarif).","triggerScenarios":"Resolving the report resource for a scan whose bundle was created without report generation — e.g. a scan that was aborted before the report step, a scan produced by a tool that only emits findings/SARIF, or a bundle loaded from a store predating report support.","commonSituations":"An agent blindly follows the resource list (report is advertised as a child) for a scan that has none; partial/crashed scans persisted without the report artifact; older stores whose bundles lack the report field after a version upgrade.","solutions":["Check availability first: resolve the scan index (security://scans/<id>) or manifest and confirm a report exists before requesting it.","Use alternative resources that are always present: security://scans/<id>/findings, .../coverage, or .../sarif.","Re-run the security scan so the report artifact is generated, then resolve the report.","Handle this error explicitly in callers and degrade to the findings listing instead of surfacing a hard failure."],"exampleFix":"// before\nconst report = await resolve(new URL(`security://scans/${id}/report`));\n// after\ntry {\n  var report = await resolve(new URL(`security://scans/${id}/report`));\n} catch {\n  report = await resolve(new URL(`security://scans/${id}/findings`));\n}","handlingStrategy":"fallback","validationCode":"async function reportAvailable(handler: SecurityProtocolHandler, scanId: string, ctx?: ResolveContext): Promise<boolean> {\n  const index = await handler.resolve(new URL(`security://scans/${scanId}`), ctx);\n  // The index page lists sub-resources; or check the manifest JSON:\n  const manifest = JSON.parse((await handler.resolve(new URL(`security://scans/${scanId}/manifest`), ctx)).content);\n  return manifest.report !== undefined || manifest.hasReport === true;\n}","typeGuard":"function bundleHasReport(bundle: { report?: string }): bundle is { report: string } {\n  return typeof bundle.report === \"string\";\n}\n// use before constructing the report URL when you hold the bundle","tryCatchPattern":"try {\n  return await handler.resolve(new URL(`security://scans/${scanId}/report`), ctx);\n} catch (err) {\n  if (err instanceof Error && /has no report$/.test(err.message)) {\n    // degrade gracefully: findings listing covers most report consumers\n    return handler.resolve(new URL(`security://scans/${scanId}/findings`), ctx);\n  }\n  throw err;\n}","preventionTips":["Treat the report as an optional artifact — check the scan index or manifest before requesting it.","Default to findings/coverage/sarif resources which are always present for completed scans.","Re-run the scan if a report is required; partial or aborted scans may lack it.","After version upgrades of the security tooling, assume older stored bundles may omit the report field."],"tags":["internal-urls","missing-artifact","optional-resource"],"backgroundTag":"resource-not-found","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}