{"record":{"id":"2fcb517729b2e926","repo":"can1357/oh-my-pi","slug":"validate-requires-a-finding-uri-or-scan-id-find","errorCode":null,"errorMessage":"validate requires a finding URI or <scan-id> <finding-id>","messagePattern":"validate requires a finding URI or <scan-id> <finding-id>","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"info","filePath":"packages/coding-agent/src/slash-commands/helpers/security.ts","lineNumber":148,"sourceCode":"\t\tarchiveExisting: options.archiveExisting,\n\t\tcredentialId: options.credentialId,\n\t\tmodel: runtime.session.model,\n\t};\n\treturn coordinatorFor(runtime).preflight(input);\n}\n\nfunction scanIdFromInput(value: string): string {\n\tconst trimmed = value.trim();\n\tconst match = trimmed.match(/^security:\\/\\/scans\\/([^/]+)/);\n\treturn match?.[1] ?? trimmed;\n}\n\nfunction findingTarget(value: string): { uri: string; scanId: string; findingId: string } {\n\tconst trimmed = value.trim();\n\tconst uriMatch = trimmed.match(/^security:\\/\\/scans\\/([^/]+)\\/findings\\/([^/]+)$/);\n\tif (uriMatch) return { uri: trimmed, scanId: uriMatch[1]!, findingId: uriMatch[2]! };\n\tconst [scanId, findingId] = parseCommandArgs(trimmed);\n\tif (!scanId || !findingId) throw new Error(\"validate requires a finding URI or <scan-id> <finding-id>\");\n\treturn { uri: `security://scans/${scanId}/findings/${findingId}`, scanId, findingId };\n}\n\nasync function showResource(runtime: SlashCommandRuntime, rest: string): Promise<void> {\n\tconst raw = rest.trim();\n\tif (!raw) throw new Error(\"show requires a scan id or security:// URI\");\n\tconst uri = raw.startsWith(\"security://\") ? raw : `security://scans/${scanIdFromInput(raw)}`;\n\tconst handler = new SecurityProtocolHandler(undefined, () => true);\n\tconst resource = await handler.resolve(parseInternalUrl(uri), { cwd: runtime.cwd });\n\tawait runtime.output(resource.content);\n}\n\nasync function importResults(runtime: SlashCommandRuntime, rest: string): Promise<void> {\n\tconst [source] = parseCommandArgs(rest);\n\tif (!source) throw new Error(\"import requires a SARIF file or Codex Security bundle directory\");\n\tconst store = await SecurityStore.openForCwd(runtime.cwd);\n\tconst absolute = path.resolve(runtime.cwd, source);\n\tconst stats = await fs.stat(absolute);","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/slash-commands/helpers/security.ts#L130-L166","documentation":"The /security validate subcommand requires a way to identify a specific finding to validate: either a full security:// URI of the form security://scans/<scan-id>/findings/<finding-id>, or two positional arguments <scan-id> <finding-id>. findingTarget() throws this error when the trimmed input matches neither form — the URI regex fails and parseCommandArgs() yields fewer than two tokens. It is a usage-guard so the command never proceeds with a malformed or missing finding reference.","triggerScenarios":"Running /security validate with no arguments; with only one token (e.g. /security validate scan-123); or with a malformed URI (e.g. missing the findings segment, wrong scheme, or trailing path parts) such that the regex ^security:\\/\\/scans\\/([^/]+)\\/findings\\/([^/]+)$ does not match and parseCommandArgs returns fewer than 2 args.","commonSituations":"Developer copies a scan id but forgets the finding id; pastes a truncated URI from a renderer that stripped it; uses a different URI scheme (https://) or an old URI shape; whitespace-only argument after command parsing stripped it.","solutions":["Pass both ids: /security validate <scan-id> <finding-id>","Or pass the full finding URI: /security validate security://scans/<scan-id>/findings/<finding-id>","Copy the URI exactly as shown by /security show or the findings list output — the regex requires exactly two non-slash segments after /scans/ and /findings/","Quote arguments containing spaces so parseCommandArgs sees two separate tokens"],"exampleFix":"// before\n/security validate scan-abc\n// after\n/security validate scan-abc finding-42\n// or\n/security validate security://scans/scan-abc/findings/finding-42","handlingStrategy":"validation","validationCode":"const SECURITY_FINDING_URI = /^security:\\/\\/scans\\/([^/]+)\\/findings\\/([^/]+)$/;\nfunction validateFindingTarget(value: string): boolean {\n  const trimmed = value.trim();\n  if (SECURITY_FINDING_URI.test(trimmed)) return true;\n  const tokens = trimmed.split(/\\s+/).filter(Boolean);\n  return tokens.length === 2;\n}\n// call before issuing: if (!validateFindingTarget(input)) prompt for missing ids","typeGuard":"function isFindingUri(value: string): value is `security://scans/${string}/findings/${string}` {\n  return /^security:\\/\\/scans\\/[^/]+\\/findings\\/[^/]+$/.test(value.trim());\n}","tryCatchPattern":"try {\n  await runSlashCommand(`/security validate ${input}`);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"validate requires\")) {\n    // surface usage: need security:// URI or <scan-id> <finding-id>\n  } else throw err;\n}","preventionTips":["Always paste the full finding URI shown by /security show or findings output","When scripting, assert both scan-id and finding-id variables are non-empty before invoking","Remember the URI regex allows no slashes inside the two id segments","Quote whitespace-containing inputs so argument parsing yields exactly two tokens"],"tags":["cli","argument-validation","slash-command","usage-error"],"backgroundTag":"missing-required-argument","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}