{"record":{"id":"ecbfb34357d6d351","repo":"can1357/oh-my-pi","slug":"invalid-number-token","errorCode":null,"errorMessage":"Invalid number: ${token}","messagePattern":"Invalid number: (.+?)","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/json-parse.ts","lineNumber":481,"sourceCode":"\t\t\t\tch === \"+\" ||\n\t\t\t\tch === \".\" ||\n\t\t\t\tch === \"e\" ||\n\t\t\t\tch === \"E\" ||\n\t\t\t\tch === \"x\" ||\n\t\t\t\tch === \"X\" ||\n\t\t\t\t(ch >= \"a\" && ch <= \"f\") ||\n\t\t\t\t(ch >= \"A\" && ch <= \"F\")\n\t\t\t) {\n\t\t\t\tthis.#i++;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tconst token = s.slice(start, this.#i);\n\t\tconst num = Number(token);\n\t\tif (Number.isNaN(num)) {\n\t\t\tif (this.#partial) return INCOMPLETE;\n\t\t\tthrow new SyntaxError(`Invalid number: ${token}`);\n\t\t}\n\t\treturn num;\n\t}\n\n\t#keyword(allowBareword: boolean): unknown {\n\t\tconst s = this.#s;\n\t\tconst i = this.#i;\n\t\tfor (const [word, value] of KEYWORDS) {\n\t\t\t// Require a non-identifier boundary so `Truex` / `nullish` are not misread\n\t\t\t// as the keyword followed by junk.\n\t\t\tif (s.startsWith(word, i) && !isIdentChar(s.charCodeAt(i + word.length))) {\n\t\t\t\tthis.#i += word.length;\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\t\tif (this.#partial) {\n\t\t\t// Incomplete / unrecognized atomic token at the streaming edge — signal the\n\t\t\t// caller to roll back to the last valid prefix instead of committing junk.","sourceCodeStart":463,"sourceCodeEnd":499,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/json-parse.ts#L463-L499","documentation":"Thrown by RelaxedJson.#number in strict mode when the scanned numeric token does not convert to a finite number via Number(token) — Number.isNaN(num) is true. The scanner greedily consumes numeric-ish characters (including hex digits a-f and x/X), so a token like '0xZZ' or a lone '-' yields NaN. In partial mode the parser returns the INCOMPLETE sentinel so enclosing containers roll back instead of committing junk.","triggerScenarios":"parseJsonWithRepair with a bad numeric token, e.g. '{\"n\": --}', '[0xg]', '{\"n\": .e}'. Note NaN/Infinity are deliberately NOT accepted as numbers (they fall into this guard) so a tool never executes with non-finite arguments.","commonSituations":"An LLM emitting a malformed number (double sign, hex digits mixed with junk); NaN/Infinity appearing in tool-call arguments where strict JSON forbids them; corrupted output from a broken serializer.","solutions":["Fix the numeric literal in the input to valid JSON number syntax","Replace NaN/Infinity/-Infinity with null or a sentinel string — the parser intentionally rejects them","Use parseStreamingJson if the token may be a partially streamed number (it rolls back instead of throwing)","Validate tool arguments against a schema (finite number) before executing the call"],"exampleFix":"// before\nconst v = parseJsonWithRepair(`{\"n\": NaN}`); // SyntaxError: Invalid number: NaN\n// after\nconst v = parseJsonWithRepair(`{\"n\": null}`); // {n: null}","handlingStrategy":"try-catch","validationCode":"// Finite-number check for fields you expect to be numeric\nfunction hasFiniteNumbers(obj: Record<string, unknown>): boolean {\n  return Object.values(obj).every(v => typeof v !== \"number\" || Number.isFinite(v));\n}","typeGuard":"function isFiniteNumber(v: unknown): v is number {\n  return typeof v === \"number\" && Number.isFinite(v);\n}","tryCatchPattern":"try {\n  const value = parseJsonWithRepair<T>(text);\n} catch (err) {\n  if (err instanceof SyntaxError && err.message.startsWith(\"Invalid number:\")) {\n    // replace NaN/Infinity with null/sentinel and re-emit, or reject the call\n  } else throw err;\n}","preventionTips":["Never put NaN/Infinity in JSON payloads — the parser rejects them by design","Validate numeric arguments are finite before executing tools","Use parseStreamingJson for partially streamed numeric tokens","Sanitize upstream data (Python None/NaN) before serializing to JSON"],"tags":["json","parse","number","nan","syntaxerror"],"backgroundTag":"json-invalid-number","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}