{"record":{"id":"1af8464fe7f57dd9","repo":"octobercms/october","slug":"broken-json-string-body-near-body","errorCode":null,"errorMessage":"Broken JSON string body near ${body}","messagePattern":"Broken JSON string body near (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"modules/system/assets/js/framework.js","lineNumber":2217,"sourceCode":"      throw new Error(\"Broken JSON syntax near \" + key);\n    }\n    getBody(str, pos) {\n      if (str[pos] === '\"' || str[pos] === \"'\") {\n        var body = str[pos];\n        for (var i = pos + 1; i < str.length; i++) {\n          if (str[i] === \"\\\\\") {\n            body += str[i];\n            if (i + 1 < str.length) body += str[i + 1];\n            i++;\n          } else if (str[i] === str[pos]) {\n            body += str[pos];\n            return {\n              originLength: body.length,\n              body\n            };\n          } else body += str[i];\n        }\n        throw new Error(\"Broken JSON string body near \" + body);\n      }\n      if (str[pos] === \"t\") {\n        if (str.indexOf(\"true\", pos) === pos) {\n          return {\n            originLength: \"true\".length,\n            body: \"true\"\n          };\n        }\n        throw new Error(\"Broken JSON boolean body near \" + str.substr(0, pos + 10));\n      }\n      if (str[pos] === \"f\") {\n        if (str.indexOf(\"f\", pos) === pos) {\n          return {\n            originLength: \"false\".length,\n            body: \"false\"\n          };\n        }\n        throw new Error(\"Broken JSON boolean body near \" + str.substr(0, pos + 10));","sourceCodeStart":2199,"sourceCodeEnd":2235,"githubUrl":"https://github.com/octobercms/october/blob/b608633a7e8922487d91a8161499020121c3b3bf/modules/system/assets/js/framework.js#L2199-L2235","documentation":"October CMS's front-end framework ships a lenient JSON parser (JsonParser in framework.js, exposed as oc.parseJSON) that accepts JavaScript object-literal syntax — single quotes, unquoted keys, trailing commas — in data attributes such as data-request-data. Its getBody() method scans one value token; when a value starts with a quote it walks forward looking for the matching closing quote. This error means the input ended before that closing quote was found, so the string value token could never be completed.","triggerScenarios":"An attribute value with an unterminated string, e.g. data-request-data=\"delay: '300\"; an apostrophe inside a single-quoted value such as name: 'it's here' (the second ' closes the string early and the rest never terminates); a value truncated mid-string because a raw double quote inside a double-quoted HTML attribute cut the attribute off; a direct oc.parseJSON(\"{'key': 'unterminated}\") call.","commonSituations":"Writing data-request-data or similar data-* attributes by hand in CMS partials; templating that interpolates user text (names with apostrophes) into attribute values; HTML produced by a WYSIWYG editor that normalizes quotes; migrating old jQuery .data('request-data', \"...\") strings into attributes.","solutions":["Add the missing closing quote that matches the opening quote of the broken string value (the message shows everything scanned so far in 'near ...').","Escape apostrophes inside single-quoted strings with a backslash (\\'), or switch to double-quoted strings inside the attribute.","If the value contains double quotes, swap the HTML attribute delimiters (data-request-data=\"...\" to data-request-data='...') so the inner quotes survive HTML parsing.","Reproduce in the browser console with oc.parseJSON(element.dataset.requestData) to see exactly which token the parser choked on."],"exampleFix":"// before\n<button data-request=\"onSave\" data-request-data=\"name: 'John's file\">Save</button>\n\n// after\n<button data-request=\"onSave\" data-request-data='name: \"John's file\"'>Save</button>","handlingStrategy":"try-catch","validationCode":"// Before calling oc.parseJSON on an attribute value, confirm quotes terminate\nfunction hasUnterminatedString(value) {\n    var inQuote = null;\n    for (var i = 0; i < value.length; i++) {\n        var ch = value[i];\n        if (inQuote) {\n            if (ch === '\\\\') { i++; continue; }\n            if (ch === inQuote) inQuote = null;\n        } else if (ch === '\"' || ch === \"'\") {\n            inQuote = ch;\n        }\n    }\n    return inQuote !== null;\n}","typeGuard":null,"tryCatchPattern":"try {\n    var data = oc.parseJSON(el.dataset.requestData);\n} catch (e) {\n    console.error('Invalid data-request-data on', el, el.dataset.requestData, e.message);\n    return; // skip the request rather than break the page\n}","preventionTips":["Standardize on double quotes inside data-* attribute values and single quotes for the HTML attribute itself.","Escape user-supplied text (apostrophes, quotes) before interpolating it into attribute values server-side.","Add a smoke test that runs oc.parseJSON over every data-request-data value in rendered fixtures."],"tags":["json","parsing","frontend","data-attribute","oc-parsejson"],"backgroundTag":"unterminated-string-literal","analyzedSha":"b608633a7e8922487d91a8161499020121c3b3bf","analyzedAt":"2026-08-21T04:24:57.515Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}