can1357/oh-my-pi · error · SyntaxError
Invalid number: ${token}
Error message
Invalid number: ${token} What it means
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.
Source
Thrown at packages/utils/src/json-parse.ts:481
ch === "+" ||
ch === "." ||
ch === "e" ||
ch === "E" ||
ch === "x" ||
ch === "X" ||
(ch >= "a" && ch <= "f") ||
(ch >= "A" && ch <= "F")
) {
this.#i++;
} else {
break;
}
}
const token = s.slice(start, this.#i);
const num = Number(token);
if (Number.isNaN(num)) {
if (this.#partial) return INCOMPLETE;
throw new SyntaxError(`Invalid number: ${token}`);
}
return num;
}
#keyword(allowBareword: boolean): unknown {
const s = this.#s;
const i = this.#i;
for (const [word, value] of KEYWORDS) {
// Require a non-identifier boundary so `Truex` / `nullish` are not misread
// as the keyword followed by junk.
if (s.startsWith(word, i) && !isIdentChar(s.charCodeAt(i + word.length))) {
this.#i += word.length;
return value;
}
}
if (this.#partial) {
// Incomplete / unrecognized atomic token at the streaming edge — signal the
// caller to roll back to the last valid prefix instead of committing junk.View on GitHub (pinned to 9690622007)
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
Example fix
// before
const v = parseJsonWithRepair(`{"n": NaN}`); // SyntaxError: Invalid number: NaN
// after
const v = parseJsonWithRepair(`{"n": null}`); // {n: null} Defensive patterns
Strategy: try-catch
Validate before calling
// Finite-number check for fields you expect to be numeric
function hasFiniteNumbers(obj: Record<string, unknown>): boolean {
return Object.values(obj).every(v => typeof v !== "number" || Number.isFinite(v));
} Type guard
function isFiniteNumber(v: unknown): v is number {
return typeof v === "number" && Number.isFinite(v);
} Try / catch
try {
const value = parseJsonWithRepair<T>(text);
} catch (err) {
if (err instanceof SyntaxError && err.message.startsWith("Invalid number:")) {
// replace NaN/Infinity with null/sentinel and re-emit, or reject the call
} else throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Unexpected trailing characters at position ${this.#i}
- Expected ':' in object
- Expected ',' or '}' in object
- Expected ',' or ']' in array
- Expected object key
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/ecbfb34357d6d351.
Report an issue: GitHub.