octobercms/october · error · Error

Broken JSON number body near ${body}

Error message

Broken JSON number body near ${body}

What it means

Thrown by JsonParser.getBody in October CMS's AJAX framework while scanning a numeric token. The scanner accepts digits, '-', '+' and '.' and only terminates a number when it hits a non-numeric character; if the digits run all the way to the end of the input, there is no closing delimiter, which means the surrounding JSON structure was truncated, so it throws with the number scanned so far.

Source

Thrown at modules/system/assets/js/framework-bundle.js:2277

            originLength: "null".length,
            body: "null"
          };
        }
        throw new Error("Broken JSON boolean body near " + str.substr(0, pos + 10));
      }
      if (str[pos] === "-" || str[pos] === "+" || str[pos] === "." || str[pos] >= "0" && str[pos] <= "9") {
        var body = "";
        for (var i = pos; i < str.length; i++) {
          if (str[i] === "-" || str[i] === "+" || str[i] === "." || str[i] >= "0" && str[i] <= "9") {
            body += str[i];
          } else {
            return {
              originLength: body.length,
              body
            };
          }
        }
        throw new Error("Broken JSON number body near " + body);
      }
      if (str[pos] === "{" || str[pos] === "[") {
        var stack = [str[pos]];
        var body = str[pos];
        for (var i = pos + 1; i < str.length; i++) {
          body += str[i];
          if (str[i] === "\\") {
            if (i + 1 < str.length) body += str[i + 1];
            i++;
          } else if (str[i] === '"') {
            if (stack[stack.length - 1] === '"') {
              stack.pop();
            } else if (stack[stack.length - 1] !== "'") {
              stack.push(str[i]);
            }
          } else if (str[i] === "'") {
            if (stack[stack.length - 1] === "'") {
              stack.pop();

View on GitHub (pinned to b608633a7e)

Solutions

  1. Close the unterminated container: append the missing } or ] (or closing quote) to the attribute value
  2. If building the value dynamically, use JSON.stringify/oc.values to serialize instead of string concatenation
  3. Lint the attribute with oc.parseJSON (or strict JSON.parse) before triggering the request to catch truncation early

Example fix

// before
el.setAttribute('data-request-data', '{page: ' + page);

// after
el.setAttribute('data-request-data', JSON.stringify({ page: page }));
Defensive patterns

Strategy: try-catch

Validate before calling

// Numbers in the attribute must be followed by a delimiter before end of input
function endsWithClosedContainer(v) {
  return /[\}"'\]]\s*$/.test(v.trim());
}

Try / catch

try {
  const data = oc.parseJSON(raw);
} catch (e) {
  if (/Broken JSON number body/.test(e.message)) {
    console.error('Truncated numeric value - missing closing } or ]:', e.message);
  }
}

Prevention

When it happens

Trigger: oc.parseJSON("{page: 3") or data-request-data="{count: 5" - any attribute value ending in a bare number with the closing brace/bracket or quote missing. The parser reaches end-of-string while still inside the number token and throws.

Common situations: Truncated attribute values from string concatenation in templates; manually edited data-request-data where the closing } was deleted; AJAX-injected HTML where the attribute got cut off; copy-pasting JSON fragments that lost their tail.

Related errors


AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21). Data as JSON: /api/errors/a9c02759ce96a4af. Report an issue: GitHub.