{"record":{"id":"995f1a8a3cc56d43","repo":"can1357/oh-my-pi","slug":"pr-repo-parsed-number-diff-index-resolv","errorCode":null,"errorMessage":"pr://${repo}/${parsed.number}/diff/${index} resolved to a missing slice (parser bug).","messagePattern":"pr://(.+?)/(.+?)/diff/(.+?) resolved to a missing slice \\(parser bug\\)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/issue-pr-protocol.ts","lineNumber":476,"sourceCode":"\t\t\tcontentType: \"text/plain\",\n\t\t\tsize: Buffer.byteLength(content, \"utf-8\"),\n\t\t\tnotes: [\n\t\t\t\tfreshness,\n\t\t\t\t`Full diff for pr://${repo}/${parsed.number} (${files.length} file${files.length === 1 ? \"\" : \"s\"})`,\n\t\t\t],\n\t\t};\n\t}\n\n\tif (parsed.mode === \"slice\") {\n\t\tconst index = parsed.index ?? 0;\n\t\tif (index < 1 || index > files.length) {\n\t\t\tthrow new Error(\n\t\t\t\t`pr://${repo}/${parsed.number}/diff/${index} is out of range; PR has ${files.length} file${files.length === 1 ? \"\" : \"s\"}. Use pr://${repo}/${parsed.number}/diff to list available indices.`,\n\t\t\t);\n\t\t}\n\t\tconst file = files[index - 1];\n\t\tif (!file) {\n\t\t\tthrow new Error(`pr://${repo}/${parsed.number}/diff/${index} resolved to a missing slice (parser bug).`);\n\t\t}\n\t\tconst content = lookup.payload.unified.slice(file.startOffset, file.endOffset);\n\t\treturn {\n\t\t\turl: url.href,\n\t\t\tcontent,\n\t\t\tcontentType: \"text/plain\",\n\t\t\tsize: Buffer.byteLength(content, \"utf-8\"),\n\t\t\tnotes: [\n\t\t\t\tfreshness,\n\t\t\t\t`Showing file ${index}/${files.length}: ${file.path}`,\n\t\t\t\t`Read all: pr://${repo}/${parsed.number}/diff/all`,\n\t\t\t],\n\t\t};\n\t}\n\n\t// mode === \"list\"\n\tconst header = `# Pull Request Diff: ${repo}#${parsed.number} (${files.length} file${files.length === 1 ? \"\" : \"s\"})`;\n\tconst body =","sourceCodeStart":458,"sourceCodeEnd":494,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/issue-pr-protocol.ts#L458-L494","documentation":"This is an internal invariant assertion in fetchAndRenderPrDiff (packages/coding-agent/src/internal-urls/issue-pr-protocol.ts:476). A pr://<repo>/<n>/diff/<index> URL was parsed with a valid slice index (it passed the 1..files.length range check), but the array lookup files[index-1] still returned undefined. Since the range check at line 469 already guarantees index-1 is a valid array position, this can only happen if the files array and the unified diff offsets are inconsistent — hence '(parser bug)'.","triggerScenarios":"Resolving pr://owner/repo/<n>/diff/<i> where parseUrl produced mode='slice' with an index that passes `index >= 1 && index <= files.length` yet files[index-1] is undefined. In practice only reachable if getOrFetchPrDiff returns a files array whose length changed between the bounds check and the element read (impossible in single-threaded sync code) or if the parsed index is a non-integer/NaN edge that slips the range comparison (e.g. index coercion quirks).","commonSituations":"Not hit by end-user mistakes; encountered by maintainers while modifying parseUrl or the PrDiffFile offset/segment extraction logic, or if a cached PR diff payload has a files array inconsistent with the unified diff it wraps (e.g. after a cache-format version change).","solutions":["Report/log this as a bug in issue-pr-protocol.ts — the message explicitly marks it a parser bug; include the URL and the PR number.","Clear the PR diff cache entry for the repo/PR so a freshly parsed payload with consistent files offsets is fetched, then retry the resolution.","Inspect parseUrl's slice-index parsing for non-integer or coerced index values and make ParsedPrDiff.index strictly an integer.","Verify the cache payload schema version matches what getOrFetchPrDiff writes; a stale-format cache can yield inconsistent files arrays."],"exampleFix":"// before\nconst index = parsed.index ?? 0;\nif (index < 1 || index > files.length) { throw ... }\nconst file = files[index - 1];\nif (!file) throw new Error(`... (parser bug).`);\n// after\nconst index = Number.isInteger(parsed.index) ? parsed.index : 0;\nif (index < 1 || index > files.length) { throw ... }\nconst file = files[index - 1];\nif (!file) throw new Error(`... (parser bug).`); // now unreachable for fractional/NaN indices","handlingStrategy":"try-catch","validationCode":"// Pre-check the slice index against the fetched payload before requesting the slice URL:\nconst listing = await prHandler.resolve(parse(`pr://${repo}/${n}/diff`));\nconst fileCount = Number((listing.content.match(/\\((\\d+) files?\\)/) ?? [])[1] ?? 0);\nif (!(index >= 1 && index <= fileCount)) throw new RangeError(`diff/${index} out of range (1..${fileCount})`);","typeGuard":"function isValidSliceIndex(index: unknown, files: readonly unknown[]): index is number {\n  return typeof index === \"number\" && Number.isInteger(index) && index >= 1 && index <= files.length;\n}","tryCatchPattern":"try {\n  const res = await prHandler.resolve(parse(`pr://${repo}/${n}/diff/${i}`));\n} catch (err) {\n  if (/resolved to a missing slice \\(parser bug\\)/.test(String(err.message))) {\n    logger.error(\"issue-pr-protocol parser bug\", { url: `pr://${repo}/${n}/diff/${i}` });\n    // fall back to full diff:\n    return await prHandler.resolve(parse(`pr://${repo}/${n}/diff/all`));\n  }\n  throw err;\n}","preventionTips":["Always fetch the diff listing first and pick an index within the reported file count.","Report any occurrence of '(parser bug)' upstream — it indicates an internal invariant break, not user error.","Pin/cache payload format expectations; clear stale caches when upgrading the library.","Cover parseUrl slice parsing with integer-index tests when modifying the protocol."],"tags":["internal-invariant","parser-bug","protocol-handler","assertion"],"backgroundTag":"internal-invariant-violation","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}