{"record":{"id":"06643ca1d9928796","repo":"can1357/oh-my-pi","slug":"invalid-scheme-list-state-stateraw-exp","errorCode":null,"errorMessage":"Invalid ${scheme}:// list state '${stateRaw}'. Expected one of: ${allowedStates.join(\", \")}.","messagePattern":"Invalid (.+?):// list state '(.+?)'\\. Expected one of: (.+?)\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/issue-pr-protocol.ts","lineNumber":83,"sourceCode":"\tlimit: number;\n\tauthor: string | undefined;\n\tlabel: string | undefined;\n}\n\ntype Parsed = ParsedSingle | ParsedList | ParsedPrDiff;\n\nconst LIST_LIMIT_DEFAULT = 30;\nconst LIST_LIMIT_MAX = 100;\n\nfunction parseListOptions(url: InternalUrl, scheme: Scheme, repo: string | undefined): ParsedList {\n\tconst stateRaw = url.searchParams.get(\"state\");\n\tconst allowedStates: ParsedList[\"state\"][] =\n\t\tscheme === \"pr\" ? [\"open\", \"closed\", \"merged\", \"all\"] : [\"open\", \"closed\", \"all\"];\n\tif (stateRaw !== null && !(allowedStates as string[]).includes(stateRaw)) {\n\t\t// Reject instead of silently falling back to \"open\": a typo'd state\n\t\t// would otherwise return the open list, indistinguishable from \"no\n\t\t// matches for the requested state\".\n\t\tthrow new Error(`Invalid ${scheme}:// list state '${stateRaw}'. Expected one of: ${allowedStates.join(\", \")}.`);\n\t}\n\tconst state = (stateRaw ?? \"open\") as ParsedList[\"state\"];\n\n\tconst limitRaw = url.searchParams.get(\"limit\");\n\tlet limit = LIST_LIMIT_DEFAULT;\n\tif (limitRaw !== null) {\n\t\tconst parsed = parsePositiveDecimalInt(limitRaw);\n\t\tif (parsed === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid ${scheme}:// list limit '${limitRaw}'. Expected a positive integer (max ${LIST_LIMIT_MAX}).`,\n\t\t\t);\n\t\t}\n\t\tlimit = Math.min(parsed, LIST_LIMIT_MAX);\n\t}\n\treturn {\n\t\tkind: \"list\",\n\t\trepo,\n\t\tstate,","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/issue-pr-protocol.ts#L65-L101","documentation":"parseListOptions validates the ?state= query parameter for issue:// and pr:// list URLs. PRs accept open|closed|merged|all; issues accept open|closed|all. An unknown value throws instead of silently falling back to 'open', because the fallback would be indistinguishable from 'no matches'.","triggerScenarios":"Calling pr://...?state=Merged (case-sensitive) or issue://...?state=merged (invalid for issues) via parseUrl.","commonSituations":"Case mistakes ('Open' vs 'open'); using the PR-only 'merged' state on issue://; typos like 'closes'.","solutions":["Use a lowercase allowed value: open, closed, merged, all for pr://; open, closed, all for issue://","Drop ?state= entirely to get the 'open' default explicitly","Fix casing in generated URLs (state is compared case-sensitively)"],"exampleFix":"// before\nresolve('pr://org/repo?state=Merged')\n// after\nresolve('pr://org/repo?state=merged')","handlingStrategy":"validation","validationCode":"const PR_STATES = ['open','closed','merged','all']\nconst ISSUE_STATES = ['open','closed','all']\nconst allowed = scheme === 'pr' ? PR_STATES : ISSUE_STATES\nif (state !== undefined && !allowed.includes(state)) throw new Error(`bad ${scheme} state: ${state}`)","typeGuard":"const isPrState = (s: string): s is 'open'|'closed'|'merged'|'all' =>\n  ['open','closed','merged','all'].includes(s)","tryCatchPattern":null,"preventionTips":["Always lowercase state values in generated URLs","Use a typed union for state instead of raw strings","Remember 'merged' is pr-only"],"tags":["validation","query-param","github"],"backgroundTag":"invalid-enum-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}