{"record":{"id":"d14926f185550b56","repo":"can1357/oh-my-pi","slug":"invalid-url-line-selector-sel","errorCode":null,"errorMessage":"Invalid URL line selector: ${sel}","messagePattern":"Invalid URL line selector: (.+?)","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/fetch.ts","lineNumber":196,"sourceCode":"\t}\n\n\tlet raw = false;\n\tlet ranges: readonly LineRange[] | undefined;\n\tfor (const sel of embedded?.sels ?? []) {\n\t\tif (sel.toLowerCase() === \"raw\") {\n\t\t\traw = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ranges !== undefined) {\n\t\t\t// Two range groups on the same URL (`…:5-10:20-30`) — combine with commas instead.\n\t\t\tthrow new ToolError(\n\t\t\t\t`URL selector has multiple range groups; combine them with commas (e.g. \\`:5-10,20-30\\`).`,\n\t\t\t);\n\t\t}\n\t\tconst parsed = parseLineRanges(sel);\n\t\tif (parsed === null) {\n\t\t\t// Shouldn't happen — isUrlSelectorToken vetted it. Belt-and-suspenders.\n\t\t\tthrow new ToolError(`Invalid URL line selector: ${sel}`);\n\t\t}\n\t\tranges = parsed;\n\t}\n\n\tif (!ranges || ranges.length === 0) return { path: urlPath, raw };\n\tif (ranges.length === 1) {\n\t\tconst r = ranges[0];\n\t\treturn {\n\t\t\tpath: urlPath,\n\t\t\traw,\n\t\t\toffset: r.startLine,\n\t\t\tlimit: r.endLine !== undefined ? r.endLine - r.startLine + 1 : undefined,\n\t\t};\n\t}\n\treturn { path: urlPath, raw, ranges };\n}\n\n/**","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/fetch.ts#L178-L214","documentation":"Belt-and-suspenders branch in parseReadUrlTarget: after isUrlSelectorToken has already vetted selector tokens, parseLineRanges is expected to always succeed. If it returns null, the token was vetted as a selector but is not a parseable line range, so this error fires. Hitting it in practice means a selector token slipped past the vetting or the range grammar changed.","triggerScenarios":"Passing a URL selector token that passes isUrlSelectorToken but fails parseLineRanges — e.g. a malformed or hypothetical future selector form like `:abc` if vetting rules drift out of sync with the range parser.","commonSituations":"Rare in practice due to pre-vetting; most likely after a version upgrade where the selector grammar changed, or from hand-crafted URLs with exotic selector tokens.","solutions":["Use a plain, well-formed range selector such as `:5` or `:5-10` (comma-join multiple ranges).","Drop the selector entirely to read the whole URL.","If you believe the selector is valid, it is a vet/parser mismatch bug — report it with the exact URL used."],"exampleFix":"// before\n\"https://example.com/file.txt:abc\"\n// after\n\"https://example.com/file.txt:1-10\"","handlingStrategy":"validation","validationCode":"function validSelector(sel: string): boolean { return /^raw$|^[0-9]+(-[0-9]+)?(,[0-9]+(-[0-9]+)?)*$/.test(sel); }\nif (!validSelector(sel)) throw new Error(`bad selector: ${sel}`);","typeGuard":"function isLineRangeSelector(sel: string): boolean { return /^[0-9]+(-[0-9]+)?(,[0-9]+(-[0-9]+)?)*$/.test(sel); }","tryCatchPattern":"try {\n  await fetchTool.readUrl(id, { url }, signal);\n} catch (e) {\n  if (e instanceof ToolError && e.message.startsWith(\"Invalid URL line selector\")) {\n    // drop the selector or fix it, then retry\n  } else throw e;\n}","preventionTips":["Build selectors only from the documented grammar: line numbers, N-M ranges, comma-joined groups, optional `raw` token.","Never pass free-text after the URL's colon.","After upgrading versions, re-validate any URL templates that embed selectors."],"tags":["url-parsing","line-ranges","fetch-tool","parser"],"backgroundTag":"invalid-selector-syntax","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}