can1357/oh-my-pi · warning

Security scan ${scanId} has no report

Error message

Security scan ${scanId} has no report

What it means

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).

Source

Thrown at packages/coding-agent/src/internal-urls/security-protocol.ts:211

				const findingId = parts[3];
				const finding = await store.getFinding(scanId, findingId);
				if (!finding) throw new Error(`Unknown security finding: ${findingId}`);
				return createSecurityResource({
					url: `security://scans/${scanId}/findings/${findingId}`,
					content: formatFinding(finding),
					contentType: "text/markdown",
				});
			}
			case "coverage":
				if (parts.length !== 3) throw new Error(`Unknown security resource: security://${parts.join("/")}`);
				return createSecurityResource({
					url: `security://scans/${scanId}/coverage`,
					content: `${JSON.stringify(bundle.scan.coverage, null, 2)}\n`,
					contentType: "application/json",
				});
			case "report":
				if (parts.length !== 3) throw new Error(`Unknown security resource: security://${parts.join("/")}`);
				if (bundle.report === undefined) throw new Error(`Security scan ${scanId} has no report`);
				return createSecurityResource({
					url: `security://scans/${scanId}/report`,
					content: bundle.report,
					contentType: "text/markdown",
				});
			case "sarif":
				if (parts.length !== 3) throw new Error(`Unknown security resource: security://${parts.join("/")}`);
				if (bundle.sarif === undefined) throw new Error(`Security scan ${scanId} has no SARIF export`);
				return createSecurityResource({
					url: `security://scans/${scanId}/sarif`,
					content: `${JSON.stringify(bundle.sarif, null, 2)}\n`,
					contentType: "application/json",
				});
			case "provenance":
				if (parts.length !== 3) throw new Error(`Unknown security resource: security://${parts.join("/")}`);
				return createSecurityResource({
					url: `security://scans/${scanId}/provenance`,
					content: `${JSON.stringify(redactPrivateSecurityMetadata(bundle.scan.provenance), null, 2)}\n`,

View on GitHub (pinned to 9690622007)

Solutions

  1. Check availability first: resolve the scan index (security://scans/<id>) or manifest and confirm a report exists before requesting it.
  2. Use alternative resources that are always present: security://scans/<id>/findings, .../coverage, or .../sarif.
  3. Re-run the security scan so the report artifact is generated, then resolve the report.
  4. Handle this error explicitly in callers and degrade to the findings listing instead of surfacing a hard failure.

Example fix

// before
const report = await resolve(new URL(`security://scans/${id}/report`));
// after
try {
  var report = await resolve(new URL(`security://scans/${id}/report`));
} catch {
  report = await resolve(new URL(`security://scans/${id}/findings`));
}
Defensive patterns

Strategy: fallback

Validate before calling

async function reportAvailable(handler: SecurityProtocolHandler, scanId: string, ctx?: ResolveContext): Promise<boolean> {
  const index = await handler.resolve(new URL(`security://scans/${scanId}`), ctx);
  // The index page lists sub-resources; or check the manifest JSON:
  const manifest = JSON.parse((await handler.resolve(new URL(`security://scans/${scanId}/manifest`), ctx)).content);
  return manifest.report !== undefined || manifest.hasReport === true;
}

Type guard

function bundleHasReport(bundle: { report?: string }): bundle is { report: string } {
  return typeof bundle.report === "string";
}
// use before constructing the report URL when you hold the bundle

Try / catch

try {
  return await handler.resolve(new URL(`security://scans/${scanId}/report`), ctx);
} catch (err) {
  if (err instanceof Error && /has no report$/.test(err.message)) {
    // degrade gracefully: findings listing covers most report consumers
    return handler.resolve(new URL(`security://scans/${scanId}/findings`), ctx);
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/812d85215745f6c9. Report an issue: GitHub.