zloirock/core-js · error · SyntaxError

Unexpected character: "${chr}" at: ${i}

Error message

Unexpected character: "${chr}" at: ${i}

What it means

Thrown by the value-parsing branch of the core-js JSON.parse polyfill when the character at the current position cannot start any JSON value (true, false, null, number, string, object, or array). Native JSON.parse calls this 'Unexpected token'; the polyfill reports the character and its index.

Source

Thrown at packages/core-js/modules/es.json.parse.js:122

    var source = this.source;
    var i = this.skip(IS_WHITESPACE, this.index);
    var fork = this.fork(i);
    var chr = at(source, i);
    if (exec(IS_NUMBER_START, chr)) return fork.number();
    switch (chr) {
      case '{':
        return fork.object();
      case '[':
        return fork.array();
      case '"':
        return fork.string();
      case 't':
        return fork.keyword(true);
      case 'f':
        return fork.keyword(false);
      case 'n':
        return fork.keyword(null);
    } throw new SyntaxError('Unexpected character: "' + chr + '" at: ' + i);
  },
  node: function (type, value, start, end, nodes) {
    return new Node(value, end, type ? null : slice(this.source, start, end), nodes);
  },
  object: function () {
    var source = this.source;
    var i = this.index + 1;
    var expectKeypair = false;
    var object = {};
    var nodes = {};
    var closed = false;
    while (i < source.length) {
      i = this.until(['"', '}'], i);
      if (at(source, i) === '}' && !expectKeypair) {
        i++;
        closed = true;
        break;
      }

View on GitHub (pinned to 84e45fba09)

Solutions

  1. Look at the character and index in the message to locate the invalid token and fix the JSON source
  2. Quote all keys with double quotes and use only double-quoted strings inside JSON
  3. Replace undefined/NaN/Infinity with null (or valid values) before serializing; use JSON.stringify which converts them to null
  4. Validate the payload with JSON.parse inside try/catch or a schema validator before relying on it; if the producer is JS, log JSON.stringify(value) instead of String(value)

Example fix

// before
const json = "{ name: 'bob', age: " + age + " }"; // age === undefined
JSON.parse(json);
// after
const json = JSON.stringify({ name: 'bob', age: age ?? null });
const parsed = JSON.parse(json);
Defensive patterns

Strategy: validation

Validate before calling

function isParseableJson(s) {
  if (typeof s !== 'string' || s.trim() === '') return false;
  try { JSON.parse(s); return true; } catch { return false; }
}
if (!isParseableJson(payload)) {
  payload = JSON.stringify(payload ?? null); // re-serialize from a real JS value
}

Type guard

function isJsonPrimitiveToken(c) {
  return c === '{' || c === '[' || c === '"' || c === '-' || (c >= '0' && c <= '9') || c === 't' || c === 'f' || c === 'n';
}

Try / catch

let value;
try {
  value = JSON.parse(raw);
} catch (e) {
  if (e instanceof SyntaxError && /Unexpected character/.test(e.message)) {
    const i = Number(e.message.match(/at: (\d+)$/)?.[1] ?? NaN);
    console.error('Invalid JSON token at index', i, 'context:', raw.slice(Math.max(0, i - 20), i + 20));
  }
  throw e;
}

Prevention

When it happens

Trigger: JSON.parse() input where a value position holds something like undefined, a bare word (NaN, Infinity, a JS identifier), single-quoted text, or a stray character, e.g. JSON.parse("{a:1}") (key not quoted), JSON.parse("undefined"), or JSON.parse("'hello'").

Common situations: Stringifying JavaScript objects with undefined/NaN/Infinity or functions and feeding the result back as JSON; hand-written JSON with unquoted keys or single quotes; templates that interpolate undefined into the JSON string; server returning a JS-object literal instead of JSON.

Related errors


AI-assisted analysis of zloirock/core-js@84e45fba09 (2026-08-30). Data as JSON: /api/errors/ce0543a890ecc26b. Report an issue: GitHub.