{"record":{"id":"708d0f3cbe4a71a5","repo":"can1357/oh-my-pi","slug":"issue-resolution-failed-message","errorCode":null,"errorMessage":"issue:// resolution failed: ${message}","messagePattern":"issue:// resolution failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/issue-pr-protocol.ts","lineNumber":553,"sourceCode":"\t\t\tconst lookup = await getOrFetchIssue({\n\t\t\t\tcwd: resolveCwd(context),\n\t\t\t\trepo: parsed.repo,\n\t\t\t\tissue: String(parsed.number),\n\t\t\t\tincludeComments: parsed.comments,\n\t\t\t\tsignal: context?.signal,\n\t\t\t\tsettings: settingsFromContext(context),\n\t\t\t});\n\t\t\treturn buildSingleResource({\n\t\t\t\turl,\n\t\t\t\tscheme: \"issue\",\n\t\t\t\tparsed,\n\t\t\t\trendered: lookup.rendered,\n\t\t\t\tstatus: lookup.status,\n\t\t\t\tfetchedAt: lookup.fetchedAt,\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tthrow new Error(`issue:// resolution failed: ${message}`);\n\t\t}\n\t}\n}\n\n/**\n * Handler for `pr://` URLs.\n */\nexport class PrProtocolHandler implements ProtocolHandler {\n\treadonly scheme = \"pr\";\n\treadonly immutable = true;\n\n\tasync resolve(url: InternalUrl, context?: ResolveContext): Promise<InternalResource> {\n\t\tif (context?.signal?.aborted) {\n\t\t\tthrow new Error(\"aborted\");\n\t\t}\n\t\tconst parsed = parseUrl(url, \"pr\");\n\t\tif (parsed.kind === \"list\") {\n\t\t\ttry {","sourceCodeStart":535,"sourceCodeEnd":571,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/issue-pr-protocol.ts#L535-L571","documentation":"Wrapper error thrown by IssueProtocolHandler.resolve (packages/coding-agent/src/internal-urls/issue-pr-protocol.ts:553) when getOrFetchIssue — the single-issue resolution path for issue://owner/repo/<n> — fails. The original error message is preserved after the `issue:// resolution failed:` prefix. This is the counterpart of the listing wrapper (1532) for a specific issue.","triggerScenarios":"Resolving issue://owner/repo/<n> (or issue://<n>) when fetching that one issue fails: issue number does not exist, repo slug wrong or inaccessible, gh CLI unauthenticated, network error, rate limit, or default-repo resolution failing for the bare issue://<n> form.","commonSituations":"Typo in issue number or repo; private repo without gh auth; issue closed/deleted and cache expired forcing a live refresh that 404s; offline; GitHub API secondary rate limits.","solutions":["Read the inner message after the `issue:// resolution failed: ` prefix — it names the real cause (404, auth, network).","Verify the issue exists: `gh issue view <n> -R owner/repo` reproduces the fetch outside this library.","Use the fully-qualified issue://owner/repo/<n> form to rule out default-repo resolution problems.","Run `gh auth login` if the inner message indicates authentication or permission failure.","If the cause is transient (network, rate limit), retry after a delay — immutable cached content may still resolve in the meantime."],"exampleFix":"// before\nconst res = await handler.resolve(parse(\"issue://acme/widgets/42\"));\n// after\ntry {\n  const res = await handler.resolve(parse(\"issue://acme/widgets/42\"));\n} catch (err) {\n  const cause = String(err.message).replace(\"issue:// resolution failed: \", \"\");\n  if (/404|not found/i.test(cause)) console.error(\"Issue does not exist\");\n  else throw err;\n}","handlingStrategy":"try-catch","validationCode":"// Verify the issue is reachable before resolving through the handler:\nconst check = await $`gh issue view ${n} -R ${owner}/${repo} --json number`.quiet().nothrow();\nif (check.exitCode !== 0) throw new Error(`issue ${n} not accessible in ${owner}/${repo}`);","typeGuard":"function isIssueResolutionFailure(err: unknown): err is Error {\n  return err instanceof Error && err.message.startsWith(\"issue:// resolution failed: \");\n}","tryCatchPattern":"try {\n  return await issueHandler.resolve(url, ctx);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"issue:// resolution failed: \")) {\n    const cause = err.message.slice(\"issue:// resolution failed: \".length);\n    if (/not found|404/i.test(cause)) return null; // permanently missing\n    if (/rate limit|timeout|network/i.test(cause)) return retryWithBackoff(() => issueHandler.resolve(url, ctx));\n    throw err;\n  }\n  throw err;\n}","preventionTips":["Authenticate gh (`gh auth login`) and confirm access to the target repo beforehand.","Use fully-qualified issue://owner/repo/<n> URLs to avoid default-repo ambiguity.","Classify the inner cause: 404 = permanent, network/rate-limit = retryable.","Check that the issue number is an integer before building the URL."],"tags":["github","network","authentication","error-wrapping","protocol-handler"],"backgroundTag":"github-fetch-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}