{"record":{"id":"e30224a973270780","repo":"can1357/oh-my-pi","slug":"unexpected-end-of-json-input","errorCode":null,"errorMessage":"Unexpected end of JSON input","messagePattern":"Unexpected end of JSON input","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/json-parse.ts","lineNumber":198,"sourceCode":" * final parse never silently accepts a half-formed tool call.\n */\nclass RelaxedJson {\n\treadonly #s: string;\n\treadonly #n: number;\n\treadonly #partial: boolean;\n\t#i = 0;\n\n\tconstructor(source: string, partial: boolean) {\n\t\tthis.#s = source;\n\t\tthis.#n = source.length;\n\t\tthis.#partial = partial;\n\t}\n\n\tparse(): unknown {\n\t\tthis.#ws();\n\t\tif (this.#i >= this.#n) {\n\t\t\tif (this.#partial) return undefined;\n\t\t\tthrow new SyntaxError(\"Unexpected end of JSON input\");\n\t\t}\n\t\tconst value = this.#value(false);\n\t\tif (value === INCOMPLETE) return undefined;\n\t\tthis.#ws();\n\t\tif (!this.#partial && this.#i < this.#n) {\n\t\t\tthrow new SyntaxError(`Unexpected trailing characters at position ${this.#i}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t#ws(): void {\n\t\tconst s = this.#s;\n\t\tfor (;;) {\n\t\t\twhile (this.#i < this.#n && isWhitespace(s.charCodeAt(this.#i))) this.#i++;\n\t\t\tif (this.#i + 1 < this.#n && s.charCodeAt(this.#i) === 0x2f /* / */) {\n\t\t\t\tconst next = s.charCodeAt(this.#i + 1);\n\t\t\t\tif (next === 0x2f /* / line comment */) {\n\t\t\t\t\tthis.#i += 2;","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/json-parse.ts#L180-L216","documentation":"RelaxedJson.parse in strict (non-partial) mode throws SyntaxError('Unexpected end of JSON input') when the input, after whitespace/comments, contains nothing to parse — i.e. an empty or whitespace/comment-only string. Unlike partial mode (which returns undefined for streaming), strict mode refuses to silently accept an empty document, since a final parse must not accept a half-formed tool call.","triggerScenarios":"Calling parseJson (strict) with an empty string, a string of only whitespace, or input consisting solely of // or /* */ comments — this.#i >= this.#n after #ws() with #partial false.","commonSituations":"Parsing an empty tool-call argument buffer from an LLM stream that got cut off before any bytes arrived; reading a truncated/empty file and feeding it to the relaxed parser; splitting a stream into chunks and parsing an empty trailing chunk in strict mode.","solutions":["Check for empty/whitespace-only input before strict parsing and handle it explicitly","Use partial mode (the partial flag/API) when parsing streaming/incomplete input","Fix the upstream producer — the stream or file should not deliver an empty payload to a final parse","Catch SyntaxError and treat as malformed input with a fallback"],"exampleFix":"// before\nconst value = parseJson(chunk); // throws on empty chunk\n// after\nconst value = chunk.trim().length === 0 ? undefined : parseJson(chunk);\n// or for streams: parseJson(chunk, { partial: true })","handlingStrategy":"type-guard","validationCode":"function isParsableInput(s: string): boolean {\n  return s.trim().length > 0;\n}\nif (!isParsableInput(raw)) return undefined; // skip strict parse of empty input","typeGuard":null,"tryCatchPattern":"let value: unknown;\ntry {\n  value = parseJson(raw); // strict\n} catch (err) {\n  if (err instanceof SyntaxError && err.message === \"Unexpected end of JSON input\") {\n    value = undefined; // empty stream chunk — handle as 'no data'\n  } else {\n    throw err;\n  }\n}","preventionTips":["Check for empty/whitespace-only input before strict parsing","Use partial mode for streaming or possibly-truncated input","Validate upstream streams/files actually delivered bytes before final parse","Distinguish 'empty input' from 'malformed input' — they throw different SyntaxErrors in this parser"],"tags":["json","parsing","syntax-error","empty-input"],"backgroundTag":"unexpected-end-of-json-input","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}