can1357/oh-my-pi · error · SyntaxError

Unterminated string

Error message

Unterminated string

What it means

Thrown by RelaxedJson.#string in strict mode when end-of-input is reached without a closing quote. In partial mode an unterminated string is returned as-is (streaming auto-close); in strict mode it throws. Note the parser does try to recover unescaped inner quotes ('it's'), so this error means the string genuinely never terminates.

Solutions

  1. Ensure the complete payload arrives before final parsing (check stream completion / content-length)
  2. Use parseStreamingJson for incomplete input — unterminated strings are auto-closed
  3. Use classifyJsonPrefix: a 'prefix' result means the string may still be closed by more data
  4. Re-generate or re-request the output if a provider truncated it

Example fix

// before
const v = parseJsonWithRepair(`{"msg": "hello`); // SyntaxError: Unterminated string
// after
const v = parseStreamingJson(`{"msg": "hello`); // {msg: 'hello'}
Defensive patterns

Strategy: validation

Validate before calling

import { classifyJsonPrefix } from "@oh-my-pi/pi-utils/json-parse";
// 'prefix' includes unterminated strings that more data may close; 'invalid' cannot be repaired
const state = classifyJsonPrefix(text);

Try / catch

try {
  const value = parseJsonWithRepair<T>(text);
} catch (err) {
  if (err instanceof SyntaxError && err.message === "Unterminated string") {
    const partial = parseStreamingJson<T>(text); // auto-closes the string
  } else throw err;
}

Prevention

When it happens

Trigger: parseJsonWithRepair with '"abc', '{"key": "value', or a quote whose closer was consumed as an escape target ('abc\\' — trailing backslash swallows the quote, runStart breaks, then EOI). Only in strict mode; parseStreamingJson never throws this.

Common situations: LLM tool-call output truncated mid-string by a token limit; a JSON string missing its closing quote in hand-written config; a payload cut by a fixed-size read or log clipping.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/ebd5c4d7735c49e6. Report an issue: GitHub.

Appendix: source

Thrown at packages/utils/src/json-parse.ts:442

						out += String.fromCharCode(parseInt(hex, 16));
						i += 4;
					} else {
						out += "\\u"; // invalid \u — keep literal
					}
					break;
				}
				default:
					out += `\\${s[i]}`; // invalid escape — keep backslash literal
			}
			i++;
			runStart = i;
		}
		out += s.slice(runStart, i);
		if (this.#partial) {
			this.#i = i;
			return out;
		}
		throw new SyntaxError("Unterminated string");
	}

	/** A quote closes a string only when the next non-space char ends a value. */
	#closesString(from: number): boolean {
		const s = this.#s;
		let k = from;
		while (k < this.#n && isWhitespace(s.charCodeAt(k))) k++;
		if (k >= this.#n) return true;
		const c = s[k];
		return c === "," || c === "}" || c === "]" || c === ":";
	}

	#number(): unknown {
		const s = this.#s;
		const start = this.#i;
		while (this.#i < this.#n) {
			const ch = s[this.#i];
			if (

View on GitHub (pinned to 9690622007)