{"record":{"id":"cfdf5096cfa6c8ed","repo":"can1357/oh-my-pi","slug":"invalid-issue-identifier-identifier-pass-an-i","errorCode":null,"errorMessage":"invalid issue identifier: ${identifier}. Pass an issue number or URL.","messagePattern":"invalid issue identifier: (.+?)\\. Pass an issue number or URL\\.","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/gh-view.ts","lineNumber":543,"sourceCode":"\tconst args = [\"pr\", \"view\", String(number)];\n\tappendRepoFlag(args, repo, String(number));\n\targs.push(\"--json\", (includeComments ? GH_PR_FIELDS : GH_PR_FIELDS_NO_COMMENTS).join(\",\"));\n\tconst data = await github.json<GhPrViewData>(cwd, args, signal, { repoProvided: true });\n\tif (includeComments && typeof data.number === \"number\") {\n\t\tdata.reviewComments = await fetchPrReviewComments(cwd, repo, data.number, signal);\n\t}\n\tconst rendered = formatPrView(data, { pr: String(number), repo, comments: includeComments });\n\treturn { rendered, sourceUrl: data.url, payload: data };\n}\n\n/**\n * Cache-aware issue/view fetcher. Used by both the `github` tool op and the\n * `issue://` protocol handler so a single shared row services both surfaces.\n */\nexport async function getOrFetchIssue(options: IssueViewLookupOptions): Promise<ViewLookupResult<GhIssueViewData>> {\n\tconst identifier = requireNonEmpty(options.issue, \"issue\");\n\tif (identifier.startsWith(\"-\")) {\n\t\tthrow new ToolError(`invalid issue identifier: ${identifier}. Pass an issue number or URL.`);\n\t}\n\tconst includeComments = options.includeComments ?? true;\n\tconst authKey = options.cacheAuthKey === undefined ? (resolveGithubCacheAuthKey() ?? null) : options.cacheAuthKey;\n\tconst urlParse = parseIssueUrl(identifier);\n\t// Prefer the URL's repo when the identifier is a full URL; fall back to the\n\t// explicit `repo` option, then to the cwd's default repo.\n\tlet repo = urlParse.repo ?? normalizeOptionalString(options.repo);\n\tlet cacheNumber = urlParse.issueNumber;\n\tif (cacheNumber === undefined) {\n\t\tcacheNumber = parsePositiveDecimalInt(identifier);\n\t}\n\tif (cacheNumber !== undefined && !repo) {\n\t\ttry {\n\t\t\trepo = await resolveDefaultRepoMemoized(options.cwd, options.signal);\n\t\t} catch {\n\t\t\t// Resolution failure leaves `repo` undefined: we'll fall through to a\n\t\t\t// direct fetch below so gh produces its own error message instead of\n\t\t\t// us masking it with a friendlier one.","sourceCodeStart":525,"sourceCodeEnd":561,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/gh-view.ts#L525-L561","documentation":"getOrFetchIssue resolves a GitHub issue number or URL to issue view data, shared by the `github` tool op and the `issue://` protocol. It rejects identifiers beginning with '-' because a leading dash is indistinguishable from a CLI flag / negation and can never be a valid issue number or URL. The throw happens before any network call, so it is a cheap input guard.","triggerScenarios":"Calling getOrFetchIssue({ issue: '-123' }) or the github/issue:// surface with a value like '-' or '-x' — typically a mangled negative number, a leaked flag argument, or an empty/partial identifier after string slicing.","commonSituations":"Scripts that join argv and accidentally pass a flag ('-v') as the issue; templated prompts that interpolate an unset variable rendering as '-'; users pasting truncated URLs losing everything before the dash.","solutions":["Pass a positive issue number ('123') or a full issue URL ('https://github.com/owner/repo/issues/123')","Strip stray leading dashes or flags from the identifier before calling","Validate the identifier is non-empty, does not start with '-', and parses as a number or GitHub issue URL"],"exampleFix":"// before\nawait getOrFetchIssue({ issue: \"-42\" });\n// after\nawait getOrFetchIssue({ issue: \"42\" });","handlingStrategy":"validation","validationCode":"function isValidIssueIdentifier(id) {\n  return typeof id === \"string\" && id.length > 0 && !id.startsWith(\"-\") && (/^\\d+$/.test(id) || /^https:\\/\\/[^/]+\\/.+\\/issues\\/\\d+/.test(id));\n}\nif (!isValidIssueIdentifier(issue)) throw new Error(`bad issue identifier: ${issue}`);","typeGuard":"const isIssueIdentifier = (v: unknown): v is string =>\n  typeof v === \"string\" && v.length > 0 && !v.startsWith(\"-\") && (/^\\d+$/.test(v) || /^https:\\/\\//.test(v));","tryCatchPattern":"try {\n  await getOrFetchIssue({ issue });\n} catch (err) {\n  if (err instanceof ToolError && err.message.startsWith(\"invalid issue identifier\")) {\n    // surface a corrected prompt or fall back to listing issues\n  } else throw err;\n}","preventionTips":["Validate issue identifiers against /^\\d+$/ or a GitHub issue URL regex before calling","Trim and sanitize CLI/argv input; never forward flags as identifiers","Check interpolated template variables are defined before building the identifier"],"tags":["input-validation","github","argument-error"],"backgroundTag":"invalid-argument-format","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}