[{"data":1,"prerenderedAt":3637},["ShallowReactive",2],{"blog-batch-en-unexpected-end-of-json-input-json-parse-error-debug":3},[4,1389,2359],{"id":5,"title":6,"author":7,"body":8,"category":1363,"date":1364,"description":1365,"draft":1366,"extension":1367,"h1":1368,"image":1369,"lastmod":1370,"locales":1371,"meta":1374,"navigation":1375,"path":1376,"promo":1377,"seo":1381,"stem":1382,"tags":1383,"__hash__":1388},"blog\u002Fen\u002Fblog\u002Fjson-parse-error-debug.md","JSON Parse Failed: 10 Common API Errors and How to Debug Them","JSON Toolbox Team",{"type":9,"value":10,"toc":1292},"minimark",[11,29,41,46,49,78,85,88,91,95,100,115,121,125,140,144,194,198,227,229,235,238,255,259,276,279,295,298,320,322,326,329,354,358,397,400,413,416,424,426,430,433,456,459,471,474,514,517,539,541,545,548,551,576,579,590,593,607,610,634,636,640,643,655,658,666,669,677,680,695,697,701,704,719,722,727,730,760,763,771,773,777,780,806,809,817,820,828,831,869,871,875,878,894,897,905,908,916,919,931,933,939,942,953,956,964,967,992,995,1031,1039,1041,1045,1048,1126,1128,1132,1241,1243,1247,1258,1266,1270],[12,13,14,15,19,20,23,24,28],"p",{},"In frontend and backend development, \"JSON parse failed\" is one of the most common errors: your code expects a JSON response, but ",[16,17,18],"code",{},"JSON.parse"," throws an exception and the console fills with red errors.",[21,22],"br",{},"\nThis article breaks down ",[25,26,27],"strong",{},"10 typical JSON parsing errors"," from real-world debugging scenarios, and provides actionable troubleshooting steps and prevention tips to help you quickly locate and fix the root cause.",[30,31,32],"blockquote",{},[12,33,34,35,40],{},"If you have a \"parse failed\" JSON sample on hand, paste it into the ",[36,37,39],"a",{"href":38},"\u002Ftools\u002Fformat\u002Fjson-editor","JSON Editor"," to automatically check syntax and highlight the exact error position.",[42,43,45],"h2",{"id":44},"overview-of-typical-error-messages","Overview of Typical Error Messages",[12,47,48],{},"Across different environments and scenarios, you might see errors like:",[50,51,52,58,63,68,73],"ul",{},[53,54,55],"li",{},[16,56,57],{},"SyntaxError: Unexpected token ':' in JSON at position 3",[53,59,60],{},[16,61,62],{},"SyntaxError: Unexpected token '\u003C' in JSON at position 0",[53,64,65],{},[16,66,67],{},"SyntaxError: Unexpected token 'u' in JSON at position 20",[53,69,70],{},[16,71,72],{},"SyntaxError: Unexpected token '\u002F' in JSON at position 2",[53,74,75],{},[16,76,77],{},"JSONParseError: Unexpected end of JSON input",[12,79,80,81,84],{},"These all mean: ",[25,82,83],{},"the input is not valid JSON",", but the underlying reasons can be very different: format errors, HTML error pages, encoding issues, truncated responses, and more.",[12,86,87],{},"Below, we break them down by scenario.",[89,90],"hr",{},[42,92,94],{"id":93},"scenario-1-backend-returns-an-html-error-page-500404","Scenario 1: Backend Returns an HTML Error Page (500\u002F404)",[96,97,99],"h3",{"id":98},"symptoms","Symptoms",[50,101,102,105,108],{},[53,103,104],{},"Your frontend calls an API expecting JSON.",[53,106,107],{},"The actual status code is 500\u002F404\u002F503, and the body is an HTML error page.",[53,109,110,111,114],{},"The frontend still calls ",[16,112,113],{},"JSON.parse(responseText)"," and fails with:",[30,116,117],{},[12,118,119],{},[16,120,62],{},[96,122,124],{"id":123},"cause","Cause",[12,126,127,128,131,132,135,136,139],{},"HTML usually starts with ",[16,129,130],{},"\u003C!DOCTYPE html>"," or ",[16,133,134],{},"\u003Chtml>",", so the first character is ",[16,137,138],{},"\u003C",", which is not valid JSON.",[96,141,143],{"id":142},"debugging-steps","Debugging Steps",[145,146,147,150,157,187],"ol",{},[53,148,149],{},"Open the browser's Network panel and find the request.",[53,151,152,153,156],{},"Check the ",[25,154,155],{},"Status"," code: is it 2xx?",[53,158,159,160,163,164,167,168],{},"Check ",[16,161,162],{},"Content-Type"," in ",[25,165,166],{},"Response Headers",":\n",[50,169,170,177],{},[53,171,172,173,176],{},"Is it ",[16,174,175],{},"application\u002Fjson","?",[53,178,179,180,183,184,176],{},"Or ",[16,181,182],{},"text\u002Fhtml"," \u002F ",[16,185,186],{},"text\u002Fplain",[53,188,189,190,193],{},"Inspect the ",[25,191,192],{},"Response Body",": is it an HTML error page?",[96,195,197],{"id":196},"solutions","Solutions",[50,199,200,220],{},[53,201,202,203,205,206],{},"Before calling ",[16,204,18],{}," on the frontend:\n",[50,207,208,211],{},[53,209,210],{},"Check that the status code is 2xx;",[53,212,213,214,216,217,219],{},"Check that ",[16,215,162],{}," includes ",[16,218,175],{},".",[53,221,222,223,226],{},"For non-2xx responses, prioritize showing error information (e.g., ",[16,224,225],{},"error.message",") instead of trying to parse JSON.",[89,228],{},[42,230,232,233],{"id":231},"scenario-2-content-type-is-not-applicationjson","Scenario 2: Content-Type Is Not ",[16,234,175],{},[96,236,99],{"id":237},"symptoms-1",[50,239,240,250],{},[53,241,242,243,245,246,131,248,219],{},"The response body \"looks like JSON\", but ",[16,244,162],{}," is ",[16,247,182],{},[16,249,186],{},[53,251,252,253,219],{},"Some frameworks\u002Fmiddleware refuse to auto-parse, or your code hesitates to safely call ",[16,254,18],{},[96,256,258],{"id":257},"causes","Causes",[50,260,261,267,270],{},[53,262,263,264,266],{},"Backend framework configuration (e.g., defaulting to ",[16,265,182],{},").",[53,268,269],{},"Reverse proxy\u002Fgateway modifying response headers.",[53,271,272,273,275],{},"Error-handling middleware returning ",[16,274,182],{}," for all errors.",[96,277,143],{"id":278},"debugging-steps-1",[145,280,281,286,289],{},[53,282,159,283,285],{},[16,284,162],{}," in the Network panel.",[53,287,288],{},"Compare response headers between working and failing endpoints.",[53,290,291,292,294],{},"Inspect backend code\u002Fconfiguration for explicit ",[16,293,162],{}," settings.",[96,296,197],{"id":297},"solutions-1",[50,299,300,311],{},[53,301,302,303,306,307,310],{},"On the backend, consistently return ",[16,304,305],{},"Content-Type: application\u002Fjson"," and encode the body as UTF-8. Many frameworks also emit ",[16,308,309],{},"application\u002Fjson; charset=utf-8","; the important part is that the response body is actually valid JSON and uses a consistent UTF-8 encoding.",[53,312,313,314,316,317,319],{},"On the frontend, be cautious when parsing responses whose ",[16,315,162],{}," is not ",[16,318,175],{},", or at least log them first.",[89,321],{},[42,323,325],{"id":324},"scenario-3-trailing-commas-single-quotes-comments-fake-json","Scenario 3: Trailing Commas \u002F Single Quotes \u002F Comments (\"Fake JSON\")",[96,327,99],{"id":328},"symptoms-2",[50,330,331,334],{},[53,332,333],{},"You copy \"JSON-like\" content from logs, docs, or chat tools.",[53,335,336,337],{},"Parsing it in code or tools fails with:\n",[50,338,339,344,349],{},[53,340,341],{},[16,342,343],{},"Unexpected token ','",[53,345,346],{},[16,347,348],{},"Unexpected token ':'",[53,350,351],{},[16,352,353],{},"Unexpected token '\u002F'",[96,355,357],{"id":356},"common-error-patterns","Common Error Patterns",[145,359,360,375,386],{},[53,361,362,365],{},[25,363,364],{},"Trailing commas",[366,367,373],"pre",{"className":368,"code":370,"language":371,"meta":372},[369],"language-json","{\n  \"name\": \"Alice\",\n  \"age\": 30,\n}\n","json","",[16,374,370],{"__ignoreMap":372},[53,376,377,380],{},[25,378,379],{},"Single quotes",[366,381,384],{"className":382,"code":383,"language":371,"meta":372},[369],"{\n  'name': 'Alice'\n}\n",[16,385,383],{"__ignoreMap":372},[53,387,388,391],{},[25,389,390],{},"Comments in JSON",[366,392,395],{"className":393,"code":394,"language":371,"meta":372},[369],"{\n  \u002F\u002F user info\n  \"name\": \"Alice\"\n}\n",[16,396,394],{"__ignoreMap":372},[96,398,143],{"id":399},"debugging-steps-2",[145,401,402,407,410],{},[53,403,404,405,219],{},"Paste the raw response\u002Ftext into the ",[36,406,39],{"href":38},[53,408,409],{},"Let the tool auto-format and highlight error positions.",[53,411,412],{},"Fix according to the hints: remove trailing commas, switch to double quotes, delete comments.",[96,414,197],{"id":415},"solutions-2",[50,417,418,421],{},[53,419,420],{},"Always run \"JSON from docs\u002Flogs\" through a validator first.",[53,422,423],{},"If you need \"config with comments\" in backend logs, use JSONC or YAML and clearly label it as \"non-standard JSON\".",[89,425],{},[42,427,429],{"id":428},"scenario-4-backend-returns-double-serialized-json-strings","Scenario 4: Backend Returns \"Double-Serialized\" JSON Strings",[96,431,99],{"id":432},"symptoms-3",[50,434,435,444,453],{},[53,436,437,438],{},"The response body is a string that contains JSON as text, e.g.:\n",[366,439,442],{"className":440,"code":441,"language":371,"meta":372},[369],"\"{\\\"key\\\":\\\"value\\\"}\"\n",[16,443,441],{"__ignoreMap":372},[53,445,446,447,449,450,452],{},"The frontend calls ",[16,448,18],{}," once and gets a string, not an object; only a second ",[16,451,18],{}," yields the object.",[53,454,455],{},"If your code assumes \"one parse gives an object\", it breaks.",[96,457,258],{"id":458},"causes-1",[50,460,461,468],{},[53,462,463,464,467],{},"The backend calls ",[16,465,466],{},"JSON.stringify"," on data that is already a JSON string.",[53,469,470],{},"Or an extra layer of wrapping happens in logs\u002Fmessage queues.",[96,472,143],{"id":473},"debugging-steps-3",[145,475,476,494],{},[53,477,478,479],{},"Inspect the raw response in the Network panel:\n",[50,480,481,487],{},[53,482,483,484,176],{},"Does it start and end with ",[16,485,486],{},"\"",[53,488,489,490,493],{},"Does it contain many ",[16,491,492],{},"\\\""," inside?",[53,495,496,497,505],{},"In the console:\n",[366,498,503],{"className":499,"code":501,"language":502,"meta":372},[500],"language-js","const once = JSON.parse(text);\nconsole.log(typeof once, once);\n","js",[16,504,501],{"__ignoreMap":372},[50,506,507],{},[53,508,509,510,513],{},"If ",[16,511,512],{},"typeof once === 'string'",", it's double-serialized.",[96,515,197],{"id":516},"solutions-3",[50,518,519,524],{},[53,520,521,522,467],{},"On the backend, avoid calling ",[16,523,466],{},[53,525,526,527,529,530,538],{},"If you must support such an interface on the frontend, parse the outer layer first and explicitly verify the result is a string before parsing again. Do not blindly call ",[16,528,18],{}," twice on every response — fix the API contract whenever possible:\n",[366,531,536],{"className":532,"code":534,"language":535,"meta":372},[533],"language-ts","export function parsePossiblyDoubleEncodedJson(text: string): unknown {\n  const first = JSON.parse(text)\n  if (typeof first !== 'string') {\n    return first\n  }\n  return JSON.parse(first)\n}\n","ts",[16,537,534],{"__ignoreMap":372},"\nUse this only when your API contract explicitly documents double encoding. Otherwise, treat it as a backend contract bug.",[89,540],{},[42,542,544],{"id":543},"scenario-5-large-json-causing-timeouts-memory-issues","Scenario 5: Large JSON Causing Timeouts \u002F Memory Issues",[12,546,547],{},"A large payload is often valid JSON. The failure is usually a performance, memory, or transport problem — not a JSON syntax error.",[96,549,99],{"id":550},"symptoms-4",[50,552,553,556],{},[53,554,555],{},"The API returns a very large JSON (several MB or more).",[53,557,558,559,167,561],{},"When the frontend calls ",[16,560,18],{},[50,562,563,566],{},[53,564,565],{},"The page freezes or even crashes;",[53,567,568,569,183,572,575],{},"Or you get ",[16,570,571],{},"Out of memory",[16,573,574],{},"Invalid argument"," errors in some environments.",[96,577,258],{"id":578},"causes-2",[50,580,581,584],{},[53,582,583],{},"Parsing huge JSON in one go puts heavy pressure on the main thread and memory.",[53,585,586,587,589],{},"Some environments (old browsers, low-end devices) have limits on single ",[16,588,18],{}," calls.",[96,591,143],{"id":592},"debugging-steps-4",[145,594,595,604],{},[53,596,597,598,183,601,266],{},"Check the response size in the Network panel (",[16,599,600],{},"Size",[16,602,603],{},"Transferred",[53,605,606],{},"Try opening the JSON file locally in an editor to see if it's similarly slow.",[96,608,197],{"id":609},"solutions-4",[50,611,612,623],{},[53,613,614,615],{},"On the backend:\n",[50,616,617,620],{},[53,618,619],{},"Use pagination or cursor-based pagination for large lists;",[53,621,622],{},"Return only necessary fields to reduce payload size.",[53,624,625,626],{},"On the frontend:\n",[50,627,628,631],{},[53,629,630],{},"Use lazy loading \u002F virtual lists for large datasets;",[53,632,633],{},"Where possible, use streaming parsing (e.g., JSON stream libraries in Node.js).",[89,635],{},[42,637,639],{"id":638},"scenario-6-encoding-issues-non-utf-8-bom","Scenario 6: Encoding Issues (Non-UTF-8, BOM)",[96,641,99],{"id":642},"symptoms-5",[50,644,645,648],{},[53,646,647],{},"The response fails to parse in some environments with vague errors.",[53,649,650,651,654],{},"Opening the text in an editor reveals a BOM (",[16,652,653],{},"EF BB BF",") or non-UTF-8 encoding.",[96,656,258],{"id":657},"causes-3",[50,659,660,663],{},[53,661,662],{},"Some backends\u002Fproxies output UTF-8 with BOM or other encodings.",[53,664,665],{},"For JSON exchanged between systems, UTF-8 is the required interoperable encoding. Producers must not add a byte order mark (BOM); parsers may choose whether to ignore one, so BOM-prefixed JSON can fail in strict parsers.",[96,667,143],{"id":668},"debugging-steps-5",[145,670,671,674],{},[53,672,673],{},"Save the response as a file and open it in an editor that shows encoding (e.g., VS Code, Notepad++).",[53,675,676],{},"Check for a BOM or non-UTF-8 encoding.",[96,678,197],{"id":679},"solutions-5",[50,681,682,685,688],{},[53,683,684],{},"Ensure the backend outputs JSON as UTF-8 without BOM.",[53,686,687],{},"Configure gateways\u002Fproxies not to add BOM or change encoding.",[53,689,690,691,694],{},"In browser Fetch scenarios, the decoding chain handles some BOM cases, but the real issue may be: the server outputting non-UTF-8, the file content entering JavaScript string as ",[16,692,693],{},"U+FEFF",", proxy\u002Ftranscoding corruption, unescaped control characters in JSON strings, or incorrect text decoding. Don't attribute all \"vague parse errors\" to BOM.",[89,696],{},[42,698,700],{"id":699},"scenario-7-truncated-json-network-interruption-timeout","Scenario 7: Truncated JSON (Network Interruption \u002F Timeout)",[96,702,99],{"id":703},"symptoms-6",[50,705,706,709],{},[53,707,708],{},"The response is cut off during transmission.",[53,710,711,712],{},"Errors like:\n",[30,713,714],{},[12,715,716],{},[16,717,718],{},"SyntaxError: Unexpected end of JSON input",[96,720,258],{"id":721},"causes-4",[50,723,724],{},[53,725,726],{},"Unstable network, timeouts, or proxy interruptions truncate the JSON.",[96,728,143],{"id":729},"debugging-steps-6",[145,731,732,751],{},[53,733,734,735],{},"Check the request status in the Network panel:\n",[50,736,737],{},[53,738,739,740,743,744,743,747,750],{},"Does it show ",[16,741,742],{},"(failed)",", ",[16,745,746],{},"timeout",[16,748,749],{},"aborted",", etc.?",[53,752,753,754,131,757,266],{},"Inspect whether the response content is clearly truncated (e.g., last character is not ",[16,755,756],{},"}",[16,758,759],{},"]",[96,761,197],{"id":762},"solutions-6",[50,764,765,768],{},[53,766,767],{},"Add retry logic on the frontend, especially for mobile\u002Funstable networks.",[53,769,770],{},"Optimize timeout and retry settings on the backend\u002Fops side.",[89,772],{},[42,774,776],{"id":775},"scenario-8-extra-text-mixed-in-prefixsuffix-garbage","Scenario 8: Extra Text Mixed In (Prefix\u002FSuffix Garbage)",[96,778,99],{"id":779},"symptoms-7",[50,781,782,800],{},[53,783,784,785,793,794],{},"The response has extra text before or after the JSON, e.g.:\n",[366,786,791],{"className":787,"code":789,"language":790,"meta":372},[788],"language-text","Data: {\"key\":\"value\"}\n","text",[16,792,789],{"__ignoreMap":372},"\nor\n",[366,795,798],{"className":796,"code":797,"language":790,"meta":372},[788],"{\"key\":\"value\"}---END---\n",[16,799,797],{"__ignoreMap":372},[53,801,802,803,805],{},"Calling ",[16,804,18],{}," on the entire string fails.",[96,807,258],{"id":808},"causes-5",[50,810,811,814],{},[53,812,813],{},"Some legacy systems\u002Fdebug logic append extra text to responses.",[53,815,816],{},"Logs\u002Fproxies add prefixes\u002Fsuffixes.",[96,818,143],{"id":819},"debugging-steps-7",[145,821,822,825],{},[53,823,824],{},"Inspect the full response text in the Network panel.",[53,826,827],{},"Confirm whether there are extra characters before\u002Fafter the JSON.",[96,829,197],{"id":830},"solutions-7",[50,832,833,836,846],{},[53,834,835],{},"Fix at the source if possible: ensure responses are pure JSON.",[53,837,838,841,842,845],{},[25,839,840],{},"Do not use a regex to extract arbitrary JSON from a mixed response in production."," JSON is nested and strings can contain braces, so a simple regular expression cannot reliably identify the intended JSON value. Problems include: top-level arrays ",[16,843,844],{},"[]",", braces inside string fields, multiple JSON fragments, greedy matching, and masking of protocol-layer bugs.",[53,847,848,849],{},"Reliable alternatives:\n",[145,850,851,854,857,860,866],{},[53,852,853],{},"Fix the API so the response body is always pure JSON.",[53,855,856],{},"Design separate endpoints for different content types.",[53,858,859],{},"If the protocol uses a known wrapper format (SSE, NDJSON, JSONP, specific log format), use the corresponding parser.",[53,861,862,863,865],{},"For ad-hoc debugging, copy the raw content into the ",[36,864,39],{"href":38}," and manually identify the JSON boundaries.",[53,867,868],{},"Log limited, sanitized response fragments in code — don't guess JSON boundaries.",[89,870],{},[42,872,874],{"id":873},"scenario-9-backend-returns-half-json-half-text-mixed-content","Scenario 9: Backend Returns \"Half JSON, Half Text\" Mixed Content",[96,876,99],{"id":877},"symptoms-8",[50,879,880,889],{},[53,881,882,883],{},"The first part of the response is JSON, the rest is plain text or HTML, e.g.:\n",[366,884,887],{"className":885,"code":886,"language":790,"meta":372},[788],"{\"success\":true}\n\u003Cscript>...\u003C\u002Fscript>\n",[16,888,886],{"__ignoreMap":372},[53,890,891,893],{},[16,892,18],{}," fails when it encounters the non-JSON part.",[96,895,258],{"id":896},"causes-6",[50,898,899,902],{},[53,900,901],{},"Error-handling logic outputs HTML\u002Fscripts after writing JSON.",[53,903,904],{},"Some frameworks append debug info on exceptions.",[96,906,143],{"id":907},"debugging-steps-8",[145,909,910,913],{},[53,911,912],{},"Inspect the full response text to see if there's content after the JSON.",[53,914,915],{},"Check backend logs for exception stacks being written to the response.",[96,917,197],{"id":918},"solutions-8",[50,920,921,924],{},[53,922,923],{},"Ensure each response is either pure JSON or pure HTML, not mixed.",[53,925,926,927,930],{},"For error responses, use a consistent JSON structure (e.g., ",[16,928,929],{},"error"," field) and avoid appending HTML.",[89,932],{},[42,934,936,937],{"id":935},"scenario-10-unvalidated-user-input-or-third-party-data-passed-to-jsonparse","Scenario 10: Unvalidated User Input or Third-Party Data Passed to ",[16,938,18],{},[96,940,99],{"id":941},"symptoms-9",[50,943,944,950],{},[53,945,946,947,949],{},"Frontend\u002Fbackend directly calls ",[16,948,18],{}," on user input, third-party webhooks, or queue messages.",[53,951,952],{},"When the data is invalid, it throws exceptions and can break the entire request flow.",[96,954,258],{"id":955},"causes-7",[50,957,958,961],{},[53,959,960],{},"Missing pre-validation: length, character set, structure, etc.",[53,962,963],{},"Assuming \"the other party will always send valid JSON\".",[96,965,143],{"id":966},"debugging-steps-9",[145,968,969,986],{},[53,970,971,972,974,975],{},"Review all ",[16,973,18],{}," call sites in your code:\n",[50,976,977,980],{},[53,978,979],{},"Is the data source trusted?",[53,981,982,983,176],{},"Is it wrapped in ",[16,984,985],{},"try...catch",[53,987,988,989,991],{},"For failing samples, paste them into the ",[36,990,39],{"href":38}," to see specific errors.",[96,993,197],{"id":994},"solutions-9",[50,996,997,1022],{},[53,998,999,1000],{},"For all external input:\n",[50,1001,1002,1005,1008,1011,1016,1019],{},[53,1003,1004],{},"Set a realistic byte-size limit before parsing untrusted input (e.g., max 1MB).",[53,1006,1007],{},"Decode input as UTF-8.",[53,1009,1010],{},"Reject malformed encoding where your platform exposes decoding errors.",[53,1012,1013,1014,219],{},"Parse inside ",[16,1015,985],{},[53,1017,1018],{},"Validate the parsed value against a schema or application rules.",[53,1020,1021],{},"Avoid logging full untrusted payloads, especially if they may contain secrets.",[53,1023,1024,1025,131,1028,219],{},"JSON allows Unicode strings — don't restrict to \"printable ASCII\" only, as that would reject valid international data like ",[16,1026,1027],{},"\"São Paulo\"",[16,1029,1030],{},"\"日本語\"",[12,1032,1033,1034,1038],{},"Once text can be parsed successfully, syntax validation is only the first step. Use ",[36,1035,1037],{"href":1036},"\u002Fblog\u002Fjson-validation-syntax-vs-schema","JSON Schema validation to check required fields, data types, and allowed values"," before accepting the data in your application.",[89,1040],{},[42,1042,1044],{"id":1043},"troubleshooting-checklist-worth-bookmarking","Troubleshooting Checklist (Worth Bookmarking)",[12,1046,1047],{},"When you hit a \"JSON parse failed\" error, troubleshoot in this order:",[145,1049,1050,1056,1064,1077,1083,1096,1102,1108],{},[53,1051,1052,1055],{},[25,1053,1054],{},"Check status code",": is it 2xx?",[53,1057,1058,1061,1062,176],{},[25,1059,1060],{},"Check Content-Type",": is it ",[16,1063,175],{},[53,1065,1066,167,1069],{},[25,1067,1068],{},"Inspect response content",[50,1070,1071,1074],{},[53,1072,1073],{},"Is it pure JSON, or HTML\u002Ftext?",[53,1075,1076],{},"Are there extra characters before\u002Fafter?",[53,1078,1079,1082],{},[25,1080,1081],{},"Check size",": is it unusually large (several MB+)?",[53,1084,1085,167,1088],{},[25,1086,1087],{},"Validate with a tool",[50,1089,1090],{},[53,1091,1092,1093,1095],{},"Paste the response into the ",[36,1094,39],{"href":38}," to automatically check syntax errors.",[53,1097,1098,1101],{},[25,1099,1100],{},"Check encoding",": is there a BOM or non-UTF-8 encoding?",[53,1103,1104,1107],{},[25,1105,1106],{},"Check logs",": does the backend log show exception stacks written to the response?",[53,1109,1110,167,1113],{},[25,1111,1112],{},"Check code",[50,1114,1115,1123],{},[53,1116,1117,1118,1120,1121,176],{},"Is ",[16,1119,18],{}," wrapped in ",[16,1122,985],{},[53,1124,1125],{},"Are you trying to parse non-2xx responses?",[89,1127],{},[42,1129,1131],{"id":1130},"how-to-prevent-json-parse-failures","How to Prevent JSON Parse Failures?",[145,1133,1134,1156,1186,1209],{},[53,1135,1136,1139],{},[25,1137,1138],{},"Standardize API Contracts",[50,1140,1141,1147,1153],{},[53,1142,1143,1144],{},"Success response: ",[16,1145,1146],{},"{ \"success\": true, \"data\": {...} }",[53,1148,1149,1150],{},"Error response: ",[16,1151,1152],{},"{ \"success\": false, \"error\": { \"code\": \"...\", \"message\": \"...\" } }",[53,1154,1155],{},"All responses are pure JSON, with no extra text.",[53,1157,1158,1161],{},[25,1159,1160],{},"Unified Parsing Logic on the Frontend",[50,1162,1163,1183],{},[53,1164,1165,1166,1169,1170],{},"Encapsulate a ",[16,1167,1168],{},"fetchJson"," utility:\n",[50,1171,1172,1174,1178],{},[53,1173,1054],{},[53,1175,159,1176],{},[16,1177,162],{},[53,1179,1180,1181],{},"Then call ",[16,1182,18],{},[53,1184,1185],{},"Route all API calls through this function to reduce repeated mistakes.",[53,1187,1188,1191],{},[25,1189,1190],{},"Logging and Monitoring",[50,1192,1193,1206],{},[53,1194,1195,1196],{},"For failed parses, log:\n",[50,1197,1198,1201,1203],{},[53,1199,1200],{},"Status code",[53,1202,162],{},[53,1204,1205],{},"First 1KB of the response (sanitized)",[53,1207,1208],{},"This makes it easier to tell whether it's a backend or network issue.",[53,1210,1211,1214],{},[25,1212,1213],{},"Use Tools Effectively",[50,1215,1216,1233],{},[53,1217,1218,1219],{},"During development:\n",[50,1220,1221,1227],{},[53,1222,1223,1224,1226],{},"Use the ",[36,1225,39],{"href":38}," to quickly validate API responses.",[53,1228,1229,1232],{},[36,1230,1231],{"href":38},"Format complex structures"," before reading them.",[53,1234,1235,1236],{},"For production issues:\n",[50,1237,1238],{},[53,1239,1240],{},"Paste problematic samples into the editor to quickly locate syntax errors.",[89,1242],{},[42,1244,1246],{"id":1245},"summary","Summary",[50,1248,1249,1252,1255],{},[53,1250,1251],{},"\"JSON parse failed\" can have many causes: HTML error pages, format errors, encoding issues, truncated responses, double serialization, and more.",[53,1253,1254],{},"A systematic troubleshooting flow (status code → Content-Type → response content → tool validation) can significantly reduce debugging time.",[53,1256,1257],{},"In the long run, standardizing API contracts, encapsulating parsing logic, and using validation tools are key to minimizing these issues.",[30,1259,1260],{},[12,1261,1262,1263,1265],{},"Next time you hit a JSON parse error, try pasting the response into the ",[36,1264,39],{"href":38}," first to quickly confirm whether it's a syntax issue, then decide whether to fix it on the frontend or escalate to backend debugging.",[42,1267,1269],{"id":1268},"whats-next","What's Next?",[50,1271,1272,1278,1285],{},[53,1273,1274,1275,219],{},"Once the text can be parsed, syntax validation is only the first layer. Learn how to use ",[36,1276,1277],{"href":1036},"JSON Schema validation to check structure, required fields, and data types",[53,1279,1280,1281,219],{},"For a foundational overview of JSON structure and syntax rules, see ",[36,1282,1284],{"href":1283},"\u002Fblog\u002Fwhat-is-json","What Is JSON?",[53,1286,1287,1288,219],{},"For API design patterns and response structure best practices, see ",[36,1289,1291],{"href":1290},"\u002Fblog\u002Fjson-best-practices","JSON Best Practices",{"title":372,"searchDepth":1293,"depth":1293,"links":1294},2,[1295,1296,1303,1310,1316,1322,1328,1334,1340,1346,1352,1359,1360,1361,1362],{"id":44,"depth":1293,"text":45},{"id":93,"depth":1293,"text":94,"children":1297},[1298,1300,1301,1302],{"id":98,"depth":1299,"text":99},3,{"id":123,"depth":1299,"text":124},{"id":142,"depth":1299,"text":143},{"id":196,"depth":1299,"text":197},{"id":231,"depth":1293,"text":1304,"children":1305},"Scenario 2: Content-Type Is Not application\u002Fjson",[1306,1307,1308,1309],{"id":237,"depth":1299,"text":99},{"id":257,"depth":1299,"text":258},{"id":278,"depth":1299,"text":143},{"id":297,"depth":1299,"text":197},{"id":324,"depth":1293,"text":325,"children":1311},[1312,1313,1314,1315],{"id":328,"depth":1299,"text":99},{"id":356,"depth":1299,"text":357},{"id":399,"depth":1299,"text":143},{"id":415,"depth":1299,"text":197},{"id":428,"depth":1293,"text":429,"children":1317},[1318,1319,1320,1321],{"id":432,"depth":1299,"text":99},{"id":458,"depth":1299,"text":258},{"id":473,"depth":1299,"text":143},{"id":516,"depth":1299,"text":197},{"id":543,"depth":1293,"text":544,"children":1323},[1324,1325,1326,1327],{"id":550,"depth":1299,"text":99},{"id":578,"depth":1299,"text":258},{"id":592,"depth":1299,"text":143},{"id":609,"depth":1299,"text":197},{"id":638,"depth":1293,"text":639,"children":1329},[1330,1331,1332,1333],{"id":642,"depth":1299,"text":99},{"id":657,"depth":1299,"text":258},{"id":668,"depth":1299,"text":143},{"id":679,"depth":1299,"text":197},{"id":699,"depth":1293,"text":700,"children":1335},[1336,1337,1338,1339],{"id":703,"depth":1299,"text":99},{"id":721,"depth":1299,"text":258},{"id":729,"depth":1299,"text":143},{"id":762,"depth":1299,"text":197},{"id":775,"depth":1293,"text":776,"children":1341},[1342,1343,1344,1345],{"id":779,"depth":1299,"text":99},{"id":808,"depth":1299,"text":258},{"id":819,"depth":1299,"text":143},{"id":830,"depth":1299,"text":197},{"id":873,"depth":1293,"text":874,"children":1347},[1348,1349,1350,1351],{"id":877,"depth":1299,"text":99},{"id":896,"depth":1299,"text":258},{"id":907,"depth":1299,"text":143},{"id":918,"depth":1299,"text":197},{"id":935,"depth":1293,"text":1353,"children":1354},"Scenario 10: Unvalidated User Input or Third-Party Data Passed to JSON.parse",[1355,1356,1357,1358],{"id":941,"depth":1299,"text":99},{"id":955,"depth":1299,"text":258},{"id":966,"depth":1299,"text":143},{"id":994,"depth":1299,"text":197},{"id":1043,"depth":1293,"text":1044},{"id":1130,"depth":1293,"text":1131},{"id":1245,"depth":1293,"text":1246},{"id":1268,"depth":1293,"text":1269},"json_tools","2026-08-24T00:00:00.000Z","A practical guide to debugging JSON parse errors in real APIs, covering typical error messages, root causes, and step-by-step troubleshooting.",false,"md",null,"\u002Fblog\u002Fcover\u002Fen\u002Fjson-parse-error-debug-cover.svg","2026-09-03T00:00:00.000Z",[1372,1373],"en","zh-CN",{},true,"\u002Fen\u002Fblog\u002Fjson-parse-error-debug",{"slug":1378,"text":1379,"btn":1380},"json-editor","🚀 Validate your JSON first:","Open JSON Editor",{"title":6,"description":1365},"en\u002Fblog\u002Fjson-parse-error-debug",[1384,39,1385,1386,1387,18],"JSON","JSON Validator","API Debugging","Error Handling","IRM6Gkn-n4JrWkRL1W1vrEVS6vc0ryQO55leFG2MLTc",{"id":1390,"title":1391,"author":7,"body":1392,"category":1363,"date":2342,"description":2343,"draft":1366,"extension":1367,"h1":1398,"image":2344,"lastmod":2342,"locales":2345,"meta":2346,"navigation":1375,"path":2347,"promo":2348,"seo":2352,"stem":2353,"tags":2354,"__hash__":2358},"blog\u002Fen\u002Fblog\u002Funexpected-end-of-json-input.md","Unexpected End of JSON Input: Causes and Fixes",{"type":9,"value":1393,"toc":2311},[1394,1399,1405,1412,1422,1435,1441,1445,1456,1459,1465,1472,1476,1484,1490,1501,1522,1526,1684,1710,1714,1717,1721,1728,1734,1738,1744,1754,1758,1761,1767,1840,1844,1850,1853,1857,1863,1869,1873,1880,1886,1889,1928,1931,1935,1938,1944,1949,1955,1958,1964,1970,1974,1977,1985,2001,2021,2025,2028,2046,2049,2065,2072,2075,2079,2084,2117,2122,2153,2157,2160,2165,2169,2172,2176,2182,2186,2189,2193,2212,2216,2223,2227,2237,2241,2244,2251,2268,2270],[1395,1396,1398],"h1",{"id":1397},"unexpected-end-of-json-input-what-it-means-and-how-to-fix-it","Unexpected End of JSON Input: What It Means and How to Fix It",[366,1400,1403],{"className":1401,"code":1402,"language":790,"meta":372},[788],"SyntaxError: Unexpected end of JSON input\n    at JSON.parse (\u003Canonymous>)\n",[16,1404,1402],{"__ignoreMap":372},[12,1406,1407,1408,1411],{},"This error says one thing: ",[25,1409,1410],{},"the parser reached the end of the string while it was still expecting more."," It never found the closing piece it needed, so it gave up at the last character.",[12,1413,1414,1415,1417,1418,1421],{},"The useful part is that the message narrows the cause considerably. An empty API response, a truncated download, and a missing ",[16,1416,756],{}," are three different problems that need three different fixes — and on modern JavaScript engines they do not even produce the same message. This guide shows how to tell them apart in about a minute, and how to fix each one in ",[16,1419,1420],{},"fetch()",", Node.js, React, and file reads.",[30,1423,1424],{},[12,1425,1426,1429,1430,1434],{},[25,1427,1428],{},"Paste it and see where it breaks:"," the ",[36,1431,1433],{"href":1432},"\u002Ftools\u002Fformat\u002Fjson-repair","JSON Repair"," tool fixes missing brackets, trailing commas, and quoting problems in your browser, and reports an error instead of guessing when a document cannot be recovered.",[12,1436,1437,1438,219],{},"For the wider picture — ten parse errors, not just this one — see ",[36,1439,6],{"href":1440},"\u002Fblog\u002Fjson-parse-error-debug",[42,1442,1444],{"id":1443},"what-does-unexpected-end-of-json-input-mean","What does \"Unexpected end of JSON input\" mean?",[12,1446,1447,1455],{},[36,1448,1452],{"href":1449,"rel":1450},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FJSON\u002Fparse",[1451],"nofollow",[16,1453,1454],{},"JSON.parse()"," reads a string and expects exactly one complete JSON value. If the string ends before that value is finished, parsing fails.",[12,1457,1458],{},"On current V8 (Node 20+, Chrome, Edge), these all produce the message:",[366,1460,1463],{"className":1461,"code":1462,"language":502,"meta":372},[500],"JSON.parse('')        \u002F\u002F SyntaxError: Unexpected end of JSON input\nJSON.parse('   ')     \u002F\u002F SyntaxError: Unexpected end of JSON input\nJSON.parse('[1,')     \u002F\u002F SyntaxError: Unexpected end of JSON input\nJSON.parse('{\"a\":')   \u002F\u002F SyntaxError: Unexpected end of JSON input\nJSON.parse('tru')     \u002F\u002F SyntaxError: Unexpected end of JSON input\n",[16,1464,1462],{"__ignoreMap":372},[12,1466,1467,1468,1471],{},"The pattern: in every case the parser was ",[25,1469,1470],{},"expecting a value"," and the input ran out — the string was empty, it was only whitespace, or it ended right after a comma, a colon, or mid-literal.",[96,1473,1475],{"id":1474},"when-you-get-a-different-message-instead","When you get a different message instead",[12,1477,1478,1479,1483],{},"A document that is cut off ",[1480,1481,1482],"em",{},"after"," a complete value produces a different, more specific error with a position:",[366,1485,1488],{"className":1486,"code":1487,"language":502,"meta":372},[500],"JSON.parse('{\"a\": 1')      \u002F\u002F SyntaxError: Expected ',' or '}' after property value in JSON at position 7\nJSON.parse('[1, 2')        \u002F\u002F SyntaxError: Expected ',' or ']' after array element in JSON at position 5\nJSON.parse('{\"a\": \"bc')    \u002F\u002F SyntaxError: Unterminated string in JSON at position 9\nJSON.parse('{\"a\":1,')      \u002F\u002F SyntaxError: Expected double-quoted property name in JSON at position 7\n",[16,1489,1487],{"__ignoreMap":372},[12,1491,1492,1493,1496,1497,1500],{},"So if you are looking at exactly ",[16,1494,1495],{},"Unexpected end of JSON input"," on a current engine, suspect ",[25,1498,1499],{},"empty input"," first, not a missing brace. Two caveats:",[50,1502,1503,1513],{},[53,1504,1505,1508,1509,1512],{},[25,1506,1507],{},"Older engines differ."," Before V8's improved JSON errors, a truncated document like ",[16,1510,1511],{},"{\"a\": 1"," also reported \"Unexpected end of JSON input\". If you are on an older runtime or see this message in a library's logs, a missing bracket is still a candidate.",[53,1514,1515,1518,1519,219],{},[25,1516,1517],{},"Other engines word it differently."," Firefox reports its own message, along the lines of ",[16,1520,1521],{},"JSON.parse: unexpected end of data at line 1 column 1",[42,1523,1525],{"id":1524},"common-causes-at-a-glance","Common causes at a glance",[1527,1528,1529,1544],"table",{},[1530,1531,1532],"thead",{},[1533,1534,1535,1538,1541],"tr",{},[1536,1537,124],"th",{},[1536,1539,1540],{},"Typical symptom",[1536,1542,1543],{},"How to confirm",[1545,1546,1547,1574,1599,1616,1651,1664],"tbody",{},[1533,1548,1549,1555,1568],{},[1550,1551,1552],"td",{},[25,1553,1554],{},"Empty response body",[1550,1556,1557,1560,1561,1564,1565],{},[16,1558,1559],{},"204 No Content",", or an endpoint that returns nothing after a successful ",[16,1562,1563],{},"DELETE","\u002F",[16,1566,1567],{},"POST",[1550,1569,1570,1573],{},[16,1571,1572],{},"text.length === 0","; status is 204\u002F205",[1533,1575,1576,1581,1590],{},[1550,1577,1578],{},[25,1579,1580],{},"Whitespace-only body",[1550,1582,1583,1584,131,1587],{},"Body is ",[16,1585,1586],{},"\" \"",[16,1588,1589],{},"\"\\n\"",[1550,1591,1592,1595,1596],{},[16,1593,1594],{},"text.trim().length === 0"," but ",[16,1597,1598],{},"text.length > 0",[1533,1600,1601,1606,1609],{},[1550,1602,1603],{},[25,1604,1605],{},"Truncated response",[1550,1607,1608],{},"Timeout, proxy buffer limit, body-size cap killed the transfer",[1550,1610,1611,1612,1615],{},"Received bytes \u003C ",[16,1613,1614],{},"Content-Length","; ends mid-value",[1533,1617,1618,1623,1633],{},[1550,1619,1620],{},[25,1621,1622],{},"Unclosed structure in hand-written JSON",[1550,1624,1625,1626,743,1628,1630,1631],{},"Missing ",[16,1627,756],{},[16,1629,759],{},", or closing ",[16,1632,486],{},[1550,1634,1635,1636,743,1639,743,1642,743,1644,1647,1648],{},"Ends with ",[16,1637,1638],{},"{",[16,1640,1641],{},"[",[16,1643,486],{},[16,1645,1646],{},",",", or ",[16,1649,1650],{},":",[1533,1652,1653,1658,1661],{},[1550,1654,1655],{},[25,1656,1657],{},"File read before the write finished",[1550,1659,1660],{},"Non-atomic write, or reading a streamed file too early",[1550,1662,1663],{},"Retry the read and it works; file grows between reads",[1533,1665,1666,1674,1677],{},[1550,1667,1668],{},[25,1669,1670,1671],{},"Parsing ",[16,1672,1673],{},"undefined",[1550,1675,1676],{},"Value came from a missing variable or an unset field",[1550,1678,1679,1680,1683],{},"Message is ",[16,1681,1682],{},"\"undefined\" is not valid JSON",", not this one",[12,1685,1686,1687,1690,1691,1694,1695,1698,1699,1702,1703,1705,1706,1709],{},"One counter-intuitive case worth knowing: ",[16,1688,1689],{},"JSON.parse(null)"," does ",[25,1692,1693],{},"not"," throw — ",[16,1696,1697],{},"null"," is coerced to the string ",[16,1700,1701],{},"\"null\""," and parses to ",[16,1704,1697],{},". ",[16,1707,1708],{},"JSON.parse(undefined)"," throws, but with a different message.",[42,1711,1713],{"id":1712},"how-to-debug-empty-vs-truncated-vs-unclosed","How to debug: empty vs truncated vs unclosed",[12,1715,1716],{},"Work through these in order. Each step eliminates one of the three causes.",[96,1718,1720],{"id":1719},"step-1-read-the-text-not-the-parsed-value","Step 1: Read the text, not the parsed value",[12,1722,1723,1724,1727],{},"Never debug this error through ",[16,1725,1726],{},"res.json()"," — it parses before you can look at anything. Read the body as text first:",[366,1729,1732],{"className":1730,"code":1731,"language":502,"meta":372},[500],"const res = await fetch(url)\nconst text = await res.text()\nconsole.log('status:', res.status, '| length:', text.length)\n",[16,1733,1731],{"__ignoreMap":372},[96,1735,1737],{"id":1736},"step-2-is-it-empty","Step 2: Is it empty?",[366,1739,1742],{"className":1740,"code":1741,"language":502,"meta":372},[500],"if (text.trim().length === 0) {\n  \u002F\u002F Empty or whitespace-only: this is the classic cause of the message.\n  console.log('empty body, status', res.status)\n}\n",[16,1743,1741],{"__ignoreMap":372},[12,1745,1746,1749,1750,1753],{},[16,1747,1748],{},"length === 0"," means no body at all. ",[16,1751,1752],{},"trim().length === 0"," with a non-zero length means whitespace only — a surprisingly common outcome when a server writes a newline.",[96,1755,1757],{"id":1756},"step-3-inspect-the-tail","Step 3: Inspect the tail",[12,1759,1760],{},"The last characters tell you which failure you have:",[366,1762,1765],{"className":1763,"code":1764,"language":502,"meta":372},[500],"console.log(JSON.stringify(text.slice(-80)))\n",[16,1766,1764],{"__ignoreMap":372},[1527,1768,1769,1779],{},[1530,1770,1771],{},[1533,1772,1773,1776],{},[1536,1774,1775],{},"Ends with",[1536,1777,1778],{},"What it means",[1545,1780,1781,1792,1803,1814,1826],{},[1533,1782,1783,1789],{},[1550,1784,1785,743,1787],{},[16,1786,1638],{},[16,1788,1641],{},[1550,1790,1791],{},"The structure was opened and never closed",[1533,1793,1794,1800],{},[1550,1795,1796,1799],{},[16,1797,1798],{},"\"a"," (an odd number of unescaped quotes)",[1550,1801,1802],{},"A string was left unterminated",[1533,1804,1805,1811],{},[1550,1806,1807,131,1809],{},[16,1808,1646],{},[16,1810,1650],{},[1550,1812,1813],{},"A value was expected and never arrived",[1533,1815,1816,1819],{},[1550,1817,1818],{},"A complete value but no closing bracket",[1550,1820,1625,1821,131,1823,1825],{},[16,1822,756],{},[16,1824,759],{}," — usually a different error message with a position",[1533,1827,1828,1837],{},[1550,1829,1830,1831,131,1834],{},"Mid-token, e.g. ",[16,1832,1833],{},"tru",[16,1835,1836],{},"12.",[1550,1838,1839],{},"The document was cut off mid-value",[96,1841,1843],{"id":1842},"step-4-compare-received-bytes-with-content-length","Step 4: Compare received bytes with Content-Length",[366,1845,1848],{"className":1846,"code":1847,"language":502,"meta":372},[500],"const declared = res.headers.get('content-length')\nconsole.log('declared:', declared, '| received:', new TextEncoder().encode(text).length)\n",[16,1849,1847],{"__ignoreMap":372},[12,1851,1852],{},"If you received fewer bytes than declared, the response was truncated in transit — a timeout, a proxy buffer limit, or a body-size cap. Re-fetch; do not try to parse half a document.",[96,1854,1856],{"id":1855},"step-5-classify-it-automatically","Step 5: Classify it automatically",[12,1858,1859,1860,1862],{},"Run this only after ",[16,1861,1454],{}," has already failed — it classifies the tail of a broken string, it does not tell you whether a document is valid.",[366,1864,1867],{"className":1865,"code":1866,"language":502,"meta":372},[500],"function inspectJsonFailure(text) {\n  const trimmed = text.trim()\n\n  if (trimmed.length === 0) {\n    return { kind: 'empty', detail: 'The input is empty or whitespace only.' }\n  }\n\n  const last = trimmed[trimmed.length - 1]\n  if ('{[\",:'.includes(last)) {\n    return { kind: 'unclosed', detail: `Input ends with \"${last}\" — a value or closing bracket is missing.` }\n  }\n\n  return { kind: 'truncated', detail: 'Input ends mid-value. Compare received bytes with Content-Length.' }\n}\n",[16,1868,1866],{"__ignoreMap":372},[42,1870,1872],{"id":1871},"how-to-fix-it-in-fetch","How to fix it in fetch()",[12,1874,1875,1876,1879],{},"The fix is to stop calling ",[16,1877,1878],{},".json()"," blindly. Read the text, handle the empty case, check the content type, then parse:",[366,1881,1884],{"className":1882,"code":1883,"language":502,"meta":372},[500],"async function fetchJson(url, init) {\n  const res = await fetch(url, init)\n  const text = await res.text()\n\n  \u002F\u002F 204 No Content and 205 Reset Content have no body by definition.\n  if (res.status === 204 || res.status === 205 || text.trim() === '') {\n    return null\n  }\n\n  const type = res.headers.get('content-type') || ''\n  if (!type.includes('application\u002Fjson')) {\n    throw new Error(`Expected JSON from ${url}, got \"${type || 'no content-type'}\"`)\n  }\n\n  try {\n    return JSON.parse(text)\n  } catch (err) {\n    throw new Error(`Invalid JSON from ${url} (${text.length} bytes): ${err.message}`)\n  }\n}\n",[16,1885,1883],{"__ignoreMap":372},[12,1887,1888],{},"Three details that matter:",[145,1890,1891,1913,1922],{},[53,1892,1893,1901,1902,1904,1905,1912],{},[25,1894,1895,1898,1899],{},[16,1896,1897],{},"res.text()"," then ",[16,1900,1454],{},", not ",[16,1903,1726],{},". You cannot inspect a body you already parsed — and ",[36,1906,1909],{"href":1907,"rel":1908},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FResponse\u002Fjson",[1451],[16,1910,1911],{},"Response.json()"," throws before you get a chance.",[53,1914,1915,1918,1919,1921],{},[25,1916,1917],{},"A 204 is a success."," Do not treat it as an error; return ",[16,1920,1697],{}," (or whatever your code uses for \"no content\").",[53,1923,1924,1927],{},[25,1925,1926],{},"Check the content type."," An HTML error page from a proxy returns 200 and parses as neither JSON nor an empty string — it fails with a different message, and this check makes the real problem obvious.",[12,1929,1930],{},"For truncated responses, the fix is not parsing: it is raising timeouts, lifting proxy body limits, or paginating the endpoint.",[42,1932,1934],{"id":1933},"how-to-fix-it-in-nodejs","How to fix it in Node.js",[12,1936,1937],{},"Reading a file:",[366,1939,1942],{"className":1940,"code":1941,"language":502,"meta":372},[500],"import { readFile } from 'node:fs\u002Fpromises'\n\nconst text = await readFile('data.json', 'utf8')\n\nif (text.trim() === '') {\n  throw new Error('data.json is empty — the write did not complete')\n}\n\nconst data = JSON.parse(text)\n",[16,1943,1941],{"__ignoreMap":372},[12,1945,1946,1947,1650],{},"Parsing a request body — note that an empty body arrives as an empty string, not as ",[16,1948,1673],{},[366,1950,1953],{"className":1951,"code":1952,"language":502,"meta":372},[500],"\u002F\u002F Express\napp.post('\u002Fwebhook', express.json(), (req, res) => {\n  \u002F\u002F req.body is {} when the body is empty, not undefined\n  if (!req.body || Object.keys(req.body).length === 0) {\n    return res.status(400).json({ error: 'EMPTY_BODY' })\n  }\n  \u002F\u002F ...\n})\n",[16,1954,1952],{"__ignoreMap":372},[12,1956,1957],{},"Reading JSON that another process is still writing is the subtle version of this bug. Write atomically so a reader never sees a half-written file:",[366,1959,1962],{"className":1960,"code":1961,"language":502,"meta":372},[500],"import { writeFile, rename } from 'node:fs\u002Fpromises'\n\nconst target = 'data.json'\nconst tmp = `${target}.tmp`\n\nawait writeFile(tmp, JSON.stringify(data, null, 2), 'utf8')\nawait rename(tmp, target) \u002F\u002F atomic on the same filesystem\n",[16,1963,1961],{"__ignoreMap":372},[12,1965,1966,1969],{},[16,1967,1968],{},"rename"," is atomic within a filesystem, so a concurrent reader sees either the old file or the new one — never a partial one.",[42,1971,1973],{"id":1972},"how-to-fix-it-in-react-and-other-frontend-code","How to fix it in React and other frontend code",[12,1975,1976],{},"The usual cause in a component is parsing during the first render, before data exists — or parsing an empty response from an endpoint that returns nothing:",[366,1978,1983],{"className":1979,"code":1981,"language":1982,"meta":372},[1980],"language-jsx","function ItemList() {\n  const [items, setItems] = useState([])\n  const [error, setError] = useState(null)\n\n  useEffect(() => {\n    let cancelled = false\n\n    ;(async () => {\n      try {\n        const res = await fetch('\u002Fapi\u002Fitems')\n        const text = await res.text()\n\n        if (cancelled) return\n\n        if (text.trim() === '') {\n          setItems([])          \u002F\u002F no content is a valid outcome, not an error\n          return\n        }\n\n        setItems(JSON.parse(text))\n      } catch (err) {\n        if (!cancelled) setError(err)\n      }\n    })()\n\n    return () => { cancelled = true }\n  }, [])\n\n  if (error) return \u003Cp>Failed to load: {error.message}\u003C\u002Fp>\n  return \u003Cul>{items.map((i) => \u003Cli key={i.id}>{i.name}\u003C\u002Fli>)}\u003C\u002Ful>\n}\n","jsx",[16,1984,1981],{"__ignoreMap":372},[12,1986,1987,1988,1996,1997,2000],{},"Two habits prevent most of these: ",[25,1989,1990,1991,1993,1994],{},"never call ",[16,1992,18],{}," on a value that might be ",[16,1995,1673],{},", and ",[25,1998,1999],{},"never parse during render"," — parse inside the effect or the loader, after you have confirmed the text exists.",[12,2002,2003,2004,2007,2008,2011,2012,2014,2015,2017,2018,2020],{},"If the JSON comes from ",[16,2005,2006],{},"localStorage",", the same rule applies: ",[16,2009,2010],{},"localStorage.getItem(key)"," returns ",[16,2013,1697],{}," when the key is missing, and ",[16,2016,1689],{}," quietly returns ",[16,2019,1697],{}," instead of the object you expected.",[42,2022,2024],{"id":2023},"validate-and-repair-the-json-in-your-browser","Validate and repair the JSON in your browser",[12,2026,2027],{},"When you have the text in front of you, two tools finish the job:",[12,2029,2030,2034,2035,2038,2039,131,2042,2045],{},[25,2031,2032],{},[36,2033,1433],{"href":1432}," — fixes trailing commas, single quotes, unquoted keys, ",[25,2036,2037],{},"missing closing brackets",", comments, and Python\u002FJS constants such as ",[16,2040,2041],{},"True",[16,2043,2044],{},"NaN",". It applies best-effort fixes, then parses the result to confirm it is valid before showing it.",[12,2047,2048],{},"Two limits to keep in mind, both stated on the tool page:",[50,2050,2051,2058],{},[53,2052,2053,2054,2057],{},"It ",[25,2055,2056],{},"cannot invent missing data",". Closing a bracket is recoverable; a document whose middle was cut off is not.",[53,2059,2060,2061,2064],{},"If the structure cannot be recovered, it ",[25,2062,2063],{},"reports an error instead of returning partial output",". That is the correct outcome for a truncated response — get the full data instead.",[12,2066,2067,2071],{},[25,2068,2069],{},[36,2070,39],{"href":38}," — paste or open the file to get live syntax validation with the error position, then format or minify. Use it to confirm a hand-written file is complete before your application reads it.",[12,2073,2074],{},"Both run in your browser, so the payload is not uploaded — which matters when the broken JSON is a production API response.",[42,2076,2078],{"id":2077},"how-to-prevent-the-error","How to prevent the error",[12,2080,2081],{},[25,2082,2083],{},"Server side:",[50,2085,2086,2096,2105,2111],{},[53,2087,2088,2091,2092,2095],{},[25,2089,2090],{},"Always return valid JSON, including errors."," ",[16,2093,2094],{},"{\"error\": {...}}"," is far easier to debug than an empty 500.",[53,2097,2098,2104],{},[25,2099,2100,2101,2103],{},"Do not set ",[16,2102,305],{}," on a 204."," If there is no body, say so with the status code and no content type.",[53,2106,2107,2110],{},[25,2108,2109],{},"Set explicit timeouts and body-size limits"," so a truncation fails loudly with a status code instead of silently cutting a document in half.",[53,2112,2113,2116],{},[25,2114,2115],{},"Write files atomically"," (temp file + rename) so no reader ever sees a partial file.",[12,2118,2119],{},[25,2120,2121],{},"Client side:",[50,2123,2124,2132,2141,2147],{},[53,2125,2126,2091,2129,219],{},[25,2127,2128],{},"Guard before parsing:",[16,2130,2131],{},"if (text && text.trim() !== '') { JSON.parse(text) }",[53,2133,2134,2140],{},[25,2135,2136,2137,2139],{},"Wrap ",[16,2138,1454],{}," in try\u002Fcatch"," and include the length and the tail of the string in the error message — that is the information you will need later.",[53,2142,2143,2146],{},[25,2144,2145],{},"Treat \"no content\" as a normal outcome"," for endpoints that can legitimately return nothing.",[53,2148,2149,2152],{},[25,2150,2151],{},"Log the offending payload safely:"," log length, status, and a truncated tail, not the whole body, which may contain tokens or personal data.",[42,2154,2156],{"id":2155},"faq","FAQ",[96,2158,1444],{"id":2159},"what-does-unexpected-end-of-json-input-mean-1",[12,2161,2162,2164],{},[16,2163,1454],{}," reached the end of the string while still expecting more content. On current engines that most often means the input was empty or whitespace-only, or ended right after a comma, colon, or mid-literal.",[96,2166,2168],{"id":2167},"is-this-error-always-caused-by-an-empty-response","Is this error always caused by an empty response?",[12,2170,2171],{},"No, but on modern V8 an empty or whitespace-only string is the most common cause of this exact wording. A document with a missing closing bracket usually produces a different message that includes a character position — unless you are on an older engine, which reported the same message for both.",[96,2173,2175],{"id":2174},"how-do-i-know-if-the-response-is-truncated","How do I know if the response is truncated?",[12,2177,2178,2179,2181],{},"Compare the bytes you received with the ",[16,2180,1614],{}," header, and look at the last ~80 characters. If the document stops mid-value, or you received fewer bytes than declared, it was truncated — re-fetch rather than trying to parse it.",[96,2183,2185],{"id":2184},"can-this-error-happen-with-otherwise-valid-json","Can this error happen with otherwise valid JSON?",[12,2187,2188],{},"Yes — if only part of a valid document arrived. A 400 KB response cut off at 300 KB is \"valid JSON\" that was never fully delivered. The other case is a file you read while it was still being written.",[96,2190,2192],{"id":2191},"how-do-i-fix-it-in-fetch","How do I fix it in fetch?",[12,2194,2195,2196,2198,2199,2201,2202,743,2205,2208,2209,2211],{},"Do not call ",[16,2197,1726],{}," directly. Read ",[16,2200,1897],{},", return early for ",[16,2203,2204],{},"204",[16,2206,2207],{},"205",", or an empty body, verify the content type, then ",[16,2210,1454],{}," inside a try\u002Fcatch that reports the length and tail of the text.",[96,2213,2215],{"id":2214},"how-do-i-fix-it-in-react","How do I fix it in React?",[12,2217,2218,2219,2222],{},"Parse inside the effect or loader, not during render, and only after confirming the text exists. Handle the empty-body case explicitly instead of letting ",[16,2220,2221],{},"JSON.parse('')"," throw on every render.",[96,2224,2226],{"id":2225},"how-do-i-fix-it-in-nodejs","How do I fix it in Node.js?",[12,2228,2229,2230,2233,2234,2236],{},"Check for an empty string after ",[16,2231,2232],{},"fs.readFile",", and write files atomically (temp file + ",[16,2235,1968],{},") so a concurrent read never sees half a file.",[96,2238,2240],{"id":2239},"can-an-online-tool-repair-truncated-json","Can an online tool repair truncated JSON?",[12,2242,2243],{},"It can repair documents with recoverable syntax problems — missing closing brackets, trailing commas, quoting issues. It cannot restore data that never arrived. For a truncated response, re-fetch from the source.",[96,2245,2247,2248,2250],{"id":2246},"why-does-jsonparsenull-not-throw","Why does ",[16,2249,1689],{}," not throw?",[12,2252,2253,2254,1698,2256,2258,2259,2261,2262,2264,2265,2267],{},"Because ",[16,2255,1697],{},[16,2257,1701],{},", which is valid JSON. It returns ",[16,2260,1697],{},". This silently masks missing values, so check for ",[16,2263,1697],{}," before parsing values from ",[16,2266,2006],{}," or optional fields.",[42,2269,1269],{"id":1268},[50,2271,2272,2280,2288,2296],{},[53,2273,2274,2277,2278,219],{},[25,2275,2276],{},"Have the broken text?"," Paste it into ",[36,2279,1433],{"href":1432},[53,2281,2282,2285,2286,219],{},[25,2283,2284],{},"Need the error position?"," Validate it in the ",[36,2287,39],{"href":38},[53,2289,2290,2293,2294,219],{},[25,2291,2292],{},"Other parse errors?"," Read ",[36,2295,6],{"href":1440},[53,2297,2298,2301,2302,2306,2307,219],{},[25,2299,2300],{},"Suspect the file itself?"," See ",[36,2303,2305],{"href":2304},"\u002Fblog\u002Fhow-to-open-json-file","How to Open, View, and Edit a JSON File"," and ",[36,2308,2310],{"href":2309},"\u002Fblog\u002Fhow-to-create-json-file","How to Create a JSON File",{"title":372,"searchDepth":1293,"depth":1293,"links":2312},[2313,2316,2317,2324,2325,2326,2327,2328,2329,2341],{"id":1443,"depth":1293,"text":1444,"children":2314},[2315],{"id":1474,"depth":1299,"text":1475},{"id":1524,"depth":1293,"text":1525},{"id":1712,"depth":1293,"text":1713,"children":2318},[2319,2320,2321,2322,2323],{"id":1719,"depth":1299,"text":1720},{"id":1736,"depth":1299,"text":1737},{"id":1756,"depth":1299,"text":1757},{"id":1842,"depth":1299,"text":1843},{"id":1855,"depth":1299,"text":1856},{"id":1871,"depth":1293,"text":1872},{"id":1933,"depth":1293,"text":1934},{"id":1972,"depth":1293,"text":1973},{"id":2023,"depth":1293,"text":2024},{"id":2077,"depth":1293,"text":2078},{"id":2155,"depth":1293,"text":2156,"children":2330},[2331,2332,2333,2334,2335,2336,2337,2338,2339],{"id":2159,"depth":1299,"text":1444},{"id":2167,"depth":1299,"text":2168},{"id":2174,"depth":1299,"text":2175},{"id":2184,"depth":1299,"text":2185},{"id":2191,"depth":1299,"text":2192},{"id":2214,"depth":1299,"text":2215},{"id":2225,"depth":1299,"text":2226},{"id":2239,"depth":1299,"text":2240},{"id":2246,"depth":1299,"text":2340},"Why does JSON.parse(null) not throw?",{"id":1268,"depth":1293,"text":1269},"2026-10-04T00:00:00.000Z","Unexpected end of JSON input: the parser ran out of text too early. Diagnose empty, truncated, or unclosed JSON, then fix it in fetch, Node, and files.","\u002Fblog\u002Fcover\u002Fen\u002Funexpected-end-of-json-input-cover.svg",[1372],{},"\u002Fen\u002Fblog\u002Funexpected-end-of-json-input",{"slug":2349,"text":2350,"btn":2351},"json-repair","Paste the broken JSON and repair it in your browser:","Open JSON Repair",{"title":1391,"description":2343},"en\u002Fblog\u002Funexpected-end-of-json-input",[1384,18,2355,2356,2357],"Debugging","JavaScript","API","AecV3R5yHNO1d9xSwwnz4gsfg4nZiUl8CveDlDsd3LM",{"id":2360,"title":2361,"author":7,"body":2362,"category":1363,"date":1364,"description":3625,"draft":1366,"extension":1367,"h1":1368,"image":3626,"lastmod":1370,"locales":3627,"meta":3628,"navigation":1375,"path":3629,"promo":3630,"seo":3633,"stem":3634,"tags":3635,"__hash__":3636},"blog\u002Fzh\u002Fblog\u002Fjson-parse-error-debug.md","JSON 解析失败怎么办？接口调试中的 10 个典型错误与排查步骤",{"type":9,"value":2363,"toc":3556},[2364,2377,2385,2388,2391,2413,2416,2424,2427,2429,2433,2436,2450,2456,2459,2471,2474,2513,2516,2544,2546,2552,2555,2572,2575,2591,2594,2609,2612,2629,2631,2635,2638,2660,2663,2695,2698,2712,2715,2723,2725,2729,2732,2754,2757,2767,2770,2804,2807,2826,2828,2832,2835,2838,2862,2865,2876,2879,2891,2894,2918,2920,2924,2927,2938,2941,2949,2952,2960,2963,2977,2979,2983,2986,3000,3003,3008,3011,3036,3039,3047,3049,3053,3056,3078,3081,3089,3092,3100,3103,3145,3147,3151,3154,3172,3175,3183,3186,3194,3197,3208,3210,3216,3219,3229,3232,3240,3243,3268,3271,3308,3314,3316,3320,3323,3399,3401,3405,3509,3511,3514,3525,3533,3536],[12,2365,2366,2367,2369,2370,2372,2373,2376],{},"在前后端开发中，“JSON 解析失败”几乎是最常见的错误之一：前端调用接口，期望拿到 JSON，结果 ",[16,2368,18],{}," 抛出异常，控制台一片红。",[21,2371],{},"\n这篇文章从真实开发场景出发，总结 ",[25,2374,2375],{},"10 类典型 JSON 解析错误","，并给出可操作的排查步骤和预防建议，帮助你快速定位问题、减少调试时间。",[30,2378,2379],{},[12,2380,2381,2382,2384],{},"如果你手边正好有一段”解析失败”的 JSON，可以先把它粘贴到 ",[36,2383,39],{"href":38}," 中，自动检查语法并查看错误位置。",[42,2386,2387],{"id":2387},"典型错误信息总览",[12,2389,2390],{},"在不同环境和场景中，你可能会看到类似的报错：",[50,2392,2393,2397,2401,2405,2409],{},[53,2394,2395],{},[16,2396,57],{},[53,2398,2399],{},[16,2400,62],{},[53,2402,2403],{},[16,2404,67],{},[53,2406,2407],{},[16,2408,72],{},[53,2410,2411],{},[16,2412,77],{},[12,2414,2415],{},"这些错误信息的共同点是：",[50,2417,2418,2421],{},[53,2419,2420],{},"都说明“输入不是合法的 JSON”；",[53,2422,2423],{},"但真正原因可能完全不同：有的是格式错误，有的是接口返回了 HTML，有的是编码问题。",[12,2425,2426],{},"下面按场景分类，逐一拆解。",[89,2428],{},[42,2430,2432],{"id":2431},"场景-1后端返回了-html-错误页500404","场景 1：后端返回了 HTML 错误页（500\u002F404）",[96,2434,2435],{"id":2435},"现象",[50,2437,2438,2441,2444],{},[53,2439,2440],{},"前端调用接口，期望 JSON 响应。",[53,2442,2443],{},"实际状态码是 500\u002F404\u002F503 等，Body 是 HTML 错误页。",[53,2445,2446,2447,2449],{},"前端仍然执行 ",[16,2448,113],{},"，报错：",[30,2451,2452],{},[12,2453,2454],{},[16,2455,62],{},[96,2457,2458],{"id":2458},"原因",[12,2460,2461,2462,2464,2465,2467,2468,2470],{},"HTML 通常以 ",[16,2463,130],{}," 或 ",[16,2466,134],{}," 开头，第一个字符是 ",[16,2469,138],{},"，不是合法 JSON。",[96,2472,2473],{"id":2473},"排查步骤",[145,2475,2476,2479,2485,2508],{},[53,2477,2478],{},"打开浏览器 Network 面板，找到该请求。",[53,2480,2481,2482,2484],{},"查看 ",[25,2483,155],{},"：是否为 2xx？",[53,2486,2481,2487,2489,2490,2492,2493],{},[25,2488,166],{}," 中的 ",[16,2491,162],{},"：\n",[50,2494,2495,2501],{},[53,2496,2497,2498,2500],{},"是否为 ",[16,2499,175],{},"？",[53,2502,2503,2504,183,2506,2500],{},"还是 ",[16,2505,182],{},[16,2507,186],{},[53,2509,2481,2510,2512],{},[25,2511,192],{},"：是否是 HTML 错误页？",[96,2514,2515],{"id":2515},"解决方式",[50,2517,2518,2538],{},[53,2519,2520,2521,2523,2524],{},"前端在 ",[16,2522,18],{}," 之前：\n",[50,2525,2526,2529],{},[53,2527,2528],{},"先检查状态码是否为 2xx；",[53,2530,2531,2532,2534,2535,2537],{},"再检查 ",[16,2533,162],{}," 是否包含 ",[16,2536,175],{},"。",[53,2539,2540,2541,2543],{},"对非 2xx 响应，优先展示错误信息（例如 ",[16,2542,225],{},"），而不是尝试解析 JSON。",[89,2545],{},[42,2547,2549,2550],{"id":2548},"场景-2content-type-不是-applicationjson","场景 2：Content-Type 不是 ",[16,2551,175],{},[96,2553,2435],{"id":2554},"现象-1",[50,2556,2557,2567],{},[53,2558,2559,2560,2562,2563,2464,2565,2537],{},"接口返回的内容“看起来像 JSON”，但 ",[16,2561,162],{}," 是 ",[16,2564,182],{},[16,2566,186],{},[53,2568,2569,2570,2537],{},"某些框架\u002F中间件会因此拒绝自动解析，或你在代码中不敢放心 ",[16,2571,18],{},[96,2573,2458],{"id":2574},"原因-1",[50,2576,2577,2583,2586],{},[53,2578,2579,2580,2582],{},"后端框架配置问题（例如默认返回 ",[16,2581,182],{},"）。",[53,2584,2585],{},"反向代理\u002F网关修改了响应头。",[53,2587,2588,2589,2537],{},"某些错误处理中间件统一返回 ",[16,2590,182],{},[96,2592,2473],{"id":2593},"排查步骤-1",[145,2595,2596,2601,2604],{},[53,2597,2598,2599,2537],{},"在 Network 面板查看 ",[16,2600,162],{},[53,2602,2603],{},"对比正常接口与异常接口的响应头差异。",[53,2605,2606,2607,2537],{},"检查后端代码\u002F配置中是否显式设置了 ",[16,2608,162],{},[96,2610,2515],{"id":2611},"解决方式-1",[50,2613,2614,2623],{},[53,2615,2616,2617,2619,2620,2622],{},"后端统一返回 ",[16,2618,305],{},"，并以 UTF-8 编码输出 body。很多框架也会输出 ",[16,2621,309],{},"；关键是响应 body 必须是合法 JSON，且编码一致。",[53,2624,2625,2626,2628],{},"前端对非 ",[16,2627,175],{}," 的响应，谨慎解析，或先记录日志再处理。",[89,2630],{},[42,2632,2634],{"id":2633},"场景-3多余逗号-单引号-注释伪-json","场景 3：多余逗号 \u002F 单引号 \u002F 注释（“伪 JSON”）",[96,2636,2435],{"id":2637},"现象-2",[50,2639,2640,2643],{},[53,2641,2642],{},"从日志、文档、聊天工具中复制了一段“看起来像 JSON”的内容。",[53,2644,2645,2646],{},"粘贴到代码或工具中解析，报错：\n",[50,2647,2648,2652,2656],{},[53,2649,2650],{},[16,2651,343],{},[53,2653,2654],{},[16,2655,348],{},[53,2657,2658],{},[16,2659,353],{},[96,2661,2662],{"id":2662},"常见错误形式",[145,2664,2665,2675,2685],{},[53,2666,2667,2670],{},[25,2668,2669],{},"末尾多余逗号",[366,2671,2673],{"className":2672,"code":370,"language":371,"meta":372},[369],[16,2674,370],{"__ignoreMap":372},[53,2676,2677,2680],{},[25,2678,2679],{},"使用单引号",[366,2681,2683],{"className":2682,"code":383,"language":371,"meta":372},[369],[16,2684,383],{"__ignoreMap":372},[53,2686,2687,2690],{},[25,2688,2689],{},"在 JSON 中写注释",[366,2691,2693],{"className":2692,"code":394,"language":371,"meta":372},[369],[16,2694,394],{"__ignoreMap":372},[96,2696,2473],{"id":2697},"排查步骤-2",[145,2699,2700,2706,2709],{},[53,2701,2702,2703,2705],{},"将原始响应\u002F文本粘贴到 ",[36,2704,39],{"href":38}," 中。",[53,2707,2708],{},"让工具自动格式化并高亮错误位置。",[53,2710,2711],{},"根据提示修复：去掉多余逗号、改为双引号、删除注释。",[96,2713,2515],{"id":2714},"解决方式-2",[50,2716,2717,2720],{},[53,2718,2719],{},"对“从文档\u002F日志复制来的 JSON”，永远先过一次校验工具。",[53,2721,2722],{},"后端日志中如果需要“带注释的配置”，使用 JSONC 或 YAML，并明确标注“非标准 JSON”。",[89,2724],{},[42,2726,2728],{"id":2727},"场景-4后端返回了双重序列化的-json-字符串","场景 4：后端返回了“双重序列化”的 JSON 字符串",[96,2730,2435],{"id":2731},"现象-3",[50,2733,2734,2742,2751],{},[53,2735,2736,2737],{},"接口返回的 Body 是一个字符串，内容是 JSON 的文本表示，例如：\n",[366,2738,2740],{"className":2739,"code":441,"language":371,"meta":372},[369],[16,2741,441],{"__ignoreMap":372},[53,2743,2744,2745,2747,2748,2750],{},"前端直接 ",[16,2746,18],{}," 一次后，得到的是字符串，而不是对象；再对这个字符串 ",[16,2749,18],{}," 才会得到对象。",[53,2752,2753],{},"如果代码逻辑假设“一次 parse 就是对象”，就会出错。",[96,2755,2458],{"id":2756},"原因-2",[50,2758,2759,2764],{},[53,2760,2761,2762,2537],{},"后端对已经是 JSON 字符串的数据又做了一次 ",[16,2763,466],{},[53,2765,2766],{},"或者在日志\u002F消息队列中存储时多包了一层。",[96,2768,2473],{"id":2769},"排查步骤-3",[145,2771,2772,2788],{},[53,2773,2774,2775],{},"在 Network 面板查看原始响应：\n",[50,2776,2777,2783],{},[53,2778,2779,2780,2782],{},"是否以 ",[16,2781,486],{}," 开头和结尾？",[53,2784,2785,2786,2500],{},"内部是否有大量 ",[16,2787,492],{},[53,2789,2790,2791,2796],{},"在控制台执行：\n",[366,2792,2794],{"className":2793,"code":501,"language":502,"meta":372},[500],[16,2795,501],{"__ignoreMap":372},[50,2797,2798],{},[53,2799,2800,2801,2803],{},"如果 ",[16,2802,512],{},"，说明是双重序列化。",[96,2805,2515],{"id":2806},"解决方式-3",[50,2808,2809,2814],{},[53,2810,2811,2812,2537],{},"后端避免对已经是 JSON 字符串的数据再次 ",[16,2813,466],{},[53,2815,2816,2817,2819,2820,2825],{},"前端如果必须兼容这种接口，先解析外层，再显式验证结果是字符串后才二次解析。不要对所有响应都盲目调用两次 ",[16,2818,18],{},"——应从源头修复接口约定：\n",[366,2821,2823],{"className":2822,"code":534,"language":535,"meta":372},[533],[16,2824,534],{"__ignoreMap":372},"\n仅在接口约定明确文档化了双重编码时使用此方法。否则应将其视为后端契约 bug。",[89,2827],{},[42,2829,2831],{"id":2830},"场景-5大-json-导致超时-内存问题","场景 5：大 JSON 导致超时 \u002F 内存问题",[12,2833,2834],{},"大 JSON 通常是合法的。失败往往是性能、内存或传输问题，而不是 JSON 语法错误。",[96,2836,2435],{"id":2837},"现象-4",[50,2839,2840,2843],{},[53,2841,2842],{},"接口返回非常大的 JSON（几 MB 甚至几十 MB）。",[53,2844,2845,2846,2848,2849],{},"前端执行 ",[16,2847,18],{}," 时：\n",[50,2850,2851,2854],{},[53,2852,2853],{},"页面卡顿甚至崩溃；",[53,2855,2856,2857,183,2859,2861],{},"或在某些环境中报 ",[16,2858,571],{},[16,2860,574],{}," 等错误。",[96,2863,2458],{"id":2864},"原因-3",[50,2866,2867,2870],{},[53,2868,2869],{},"一次性解析超大 JSON 对主线程和内存压力很大。",[53,2871,2872,2873,2875],{},"某些环境（如旧浏览器、低配设备）对单次 ",[16,2874,18],{}," 的大小有限制。",[96,2877,2473],{"id":2878},"排查步骤-4",[145,2880,2881,2888],{},[53,2882,2883,2884,183,2886,2582],{},"查看响应大小（Network 面板中的 ",[16,2885,600],{},[16,2887,603],{},[53,2889,2890],{},"尝试在本地用工具打开该 JSON 文件，看是否同样卡顿。",[96,2892,2515],{"id":2893},"解决方式-4",[50,2895,2896,2907],{},[53,2897,2898,2899],{},"后端：\n",[50,2900,2901,2904],{},[53,2902,2903],{},"对大列表使用分页或游标分页；",[53,2905,2906],{},"只返回必要字段，减少冗余。",[53,2908,2909,2910],{},"前端：\n",[50,2911,2912,2915],{},[53,2913,2914],{},"对大 JSON 做懒加载\u002F虚拟列表；",[53,2916,2917],{},"如可能，使用流式解析（Node.js 中可用 stream + JSON stream 解析库）。",[89,2919],{},[42,2921,2923],{"id":2922},"场景-6编码问题非-utf-8bom-头等","场景 6：编码问题（非 UTF-8、BOM 头等）",[96,2925,2435],{"id":2926},"现象-5",[50,2928,2929,2932],{},[53,2930,2931],{},"接口返回的文本在某些环境下解析失败，报错信息含糊。",[53,2933,2934,2935,2937],{},"在文本编辑器中打开，发现文件带有 BOM 头（",[16,2936,653],{},"）或编码不是 UTF-8。",[96,2939,2458],{"id":2940},"原因-4",[50,2942,2943,2946],{},[53,2944,2945],{},"某些后端\u002F代理在输出时使用了带 BOM 的 UTF-8 或其他编码。",[53,2947,2948],{},"在系统间交换的 JSON 必须使用 UTF-8 编码。生产端不得添加字节序标记（BOM）；解析端可以选择忽略 BOM，但不能依赖所有解析器都容忍它。",[96,2950,2473],{"id":2951},"排查步骤-5",[145,2953,2954,2957],{},[53,2955,2956],{},"将响应保存为文件，用支持编码查看的编辑器打开（如 VS Code、Notepad++）。",[53,2958,2959],{},"检查是否有 BOM 头，或编码是否为 UTF-8。",[96,2961,2515],{"id":2962},"解决方式-5",[50,2964,2965,2968,2971],{},[53,2966,2967],{},"后端统一使用无 BOM 的 UTF-8 输出 JSON。",[53,2969,2970],{},"在网关\u002F代理层确保不额外添加 BOM 或改变编码。",[53,2972,2973,2974,2976],{},"在浏览器 Fetch 场景中，解码链会处理部分 BOM 情况，但真正问题可能是：服务器输出了非 UTF-8、文件内容以 ",[16,2975,693],{}," 进入 JavaScript 字符串、代理\u002F转码层破坏了字节、JSON 字符串内有未转义控制字符、或文本被错误解码。不要把所有\"模糊 parse error\"都归因于 BOM。",[89,2978],{},[42,2980,2982],{"id":2981},"场景-7截断的-json网络中断超时","场景 7：截断的 JSON（网络中断\u002F超时）",[96,2984,2435],{"id":2985},"现象-6",[50,2987,2988,2991],{},[53,2989,2990],{},"接口在传输过程中被中断，响应不完整。",[53,2992,2993,2994],{},"报错类似：\n",[30,2995,2996],{},[12,2997,2998],{},[16,2999,718],{},[96,3001,2458],{"id":3002},"原因-5",[50,3004,3005],{},[53,3006,3007],{},"网络不稳定、超时、代理中断等导致 JSON 被截断。",[96,3009,2473],{"id":3010},"排查步骤-6",[145,3012,3013,3029],{},[53,3014,3015,3016],{},"查看 Network 面板中该请求的状态：\n",[50,3017,3018],{},[53,3019,3020,3021,3023,3024,3023,3026,3028],{},"是否显示 ",[16,3022,742],{},"、",[16,3025,746],{},[16,3027,749],{}," 等？",[53,3030,3031,3032,2464,3034,2582],{},"查看响应内容是否明显截断（例如最后一个字符不是 ",[16,3033,756],{},[16,3035,759],{},[96,3037,2515],{"id":3038},"解决方式-6",[50,3040,3041,3044],{},[53,3042,3043],{},"前端增加重试逻辑（尤其是移动端\u002F不稳定网络）。",[53,3045,3046],{},"后端\u002F运维侧优化超时设置和重试策略。",[89,3048],{},[42,3050,3052],{"id":3051},"场景-8混入额外文本前后缀垃圾数据","场景 8：混入额外文本（前后缀垃圾数据）",[96,3054,2435],{"id":3055},"现象-7",[50,3057,3058,3072],{},[53,3059,3060,3061,3066,3067],{},"响应前后被加上了额外文本，例如：\n",[366,3062,3064],{"className":3063,"code":789,"language":790,"meta":372},[788],[16,3065,789],{"__ignoreMap":372},"\n或\n",[366,3068,3070],{"className":3069,"code":797,"language":790,"meta":372},[788],[16,3071,797],{"__ignoreMap":372},[53,3073,3074,3075,3077],{},"直接 ",[16,3076,18],{}," 整个字符串会失败。",[96,3079,2458],{"id":3080},"原因-6",[50,3082,3083,3086],{},[53,3084,3085],{},"某些旧系统\u002F调试逻辑在响应前后拼接了额外信息。",[53,3087,3088],{},"日志\u002F代理层添加了前缀\u002F后缀。",[96,3090,2473],{"id":3091},"排查步骤-7",[145,3093,3094,3097],{},[53,3095,3096],{},"在 Network 面板查看完整响应文本。",[53,3098,3099],{},"确认是否前后有多余字符。",[96,3101,2515],{"id":3102},"解决方式-7",[50,3104,3105,3108,3122],{},[53,3106,3107],{},"尽量从源头修复，保证响应是”纯 JSON”。",[53,3109,3110,3113,3114,3116,3117,2464,3119,3121],{},[25,3111,3112],{},"不要在生产环境中用正则从混合响应中提取任意 JSON。"," JSON 是嵌套结构，字符串中可以包含花括号，简单的正则表达式无法可靠识别目标 JSON 值。问题包括：顶层可能是数组 ",[16,3115,844],{},"、字符串字段内可能有 ",[16,3118,1638],{},[16,3120,756],{},"、文本中可能有多个 JSON 片段、贪婪匹配可能吞掉不属于 JSON 的内容、以及掩盖协议层应修复的问题。",[53,3123,3124,3125],{},"可靠的替代方案：\n",[145,3126,3127,3130,3133,3136,3142],{},[53,3128,3129],{},"修复 API，使响应 body 始终为纯 JSON。",[53,3131,3132],{},"为不同内容类型设计不同的 endpoint。",[53,3134,3135],{},"如果协议采用已知的包装格式（SSE、NDJSON、JSONP 或特定日志格式），使用对应的 parser。",[53,3137,3138,3139,3141],{},"临时排查时，将原始内容复制到 ",[36,3140,39],{"href":38}," 中，人工找出 JSON 边界。",[53,3143,3144],{},"在代码中记录有限的、脱敏的响应片段，不要盲目猜测 JSON 边界。",[89,3146],{},[42,3148,3150],{"id":3149},"场景-9后端返回了半-json-半文本的混合内容","场景 9：后端返回了“半 JSON 半文本”的混合内容",[96,3152,2435],{"id":3153},"现象-8",[50,3155,3156,3159,3167],{},[53,3157,3158],{},"响应前半部分是 JSON，后半部分是纯文本或 HTML。",[53,3160,3161,3162],{},"例如：\n",[366,3163,3165],{"className":3164,"code":886,"language":790,"meta":372},[788],[16,3166,886],{"__ignoreMap":372},[53,3168,3169,3171],{},[16,3170,18],{}," 只能处理纯 JSON，遇到后面部分就报错。",[96,3173,2458],{"id":3174},"原因-7",[50,3176,3177,3180],{},[53,3178,3179],{},"错误处理逻辑在输出 JSON 后又输出了 HTML\u002F脚本。",[53,3181,3182],{},"某些框架在异常时会追加调试信息。",[96,3184,2473],{"id":3185},"排查步骤-8",[145,3187,3188,3191],{},[53,3189,3190],{},"查看完整响应文本，确认是否在 JSON 之后还有内容。",[53,3192,3193],{},"检查后端日志，看是否有异常堆栈被输出到响应中。",[96,3195,2515],{"id":3196},"解决方式-8",[50,3198,3199,3202],{},[53,3200,3201],{},"后端确保每个响应要么是纯 JSON，要么是纯 HTML，不要混用。",[53,3203,3204,3205,3207],{},"对错误响应，统一使用 JSON 结构（如 ",[16,3206,929],{}," 字段），不再追加 HTML。",[89,3209],{},[42,3211,3213,3214],{"id":3212},"场景-10用户输入或第三方数据未校验就-jsonparse","场景 10：用户输入或第三方数据未校验就 ",[16,3215,18],{},[96,3217,2435],{"id":3218},"现象-9",[50,3220,3221,3226],{},[53,3222,3223,3224,2537],{},"前端\u002F后端直接对用户输入、第三方 Webhook、消息队列中的数据调用 ",[16,3225,18],{},[53,3227,3228],{},"一旦数据不合法，就导致异常，甚至影响整个请求处理流程。",[96,3230,2458],{"id":3231},"原因-8",[50,3233,3234,3237],{},[53,3235,3236],{},"缺少前置校验：长度、字符集、结构等。",[53,3238,3239],{},"假设“对方一定会发合法 JSON”。",[96,3241,2473],{"id":3242},"排查步骤-9",[145,3244,3245,3262],{},[53,3246,3247,3248,3250,3251],{},"检查代码中所有 ",[16,3249,18],{}," 调用点：\n",[50,3252,3253,3256],{},[53,3254,3255],{},"数据来源是否可信？",[53,3257,3258,3259,3261],{},"是否有 ",[16,3260,985],{}," 包裹？",[53,3263,3264,3265,3267],{},"对报错的输入样本，粘贴到 ",[36,3266,39],{"href":38}," 中查看具体错误。",[96,3269,2515],{"id":3270},"解决方式-9",[50,3272,3273,3301],{},[53,3274,3275,3276],{},"对所有外部输入：\n",[50,3277,3278,3281,3284,3287,3295,3298],{},[53,3279,3280],{},"在解析前设置合理的字节大小限制（例如最大 1MB）。",[53,3282,3283],{},"以 UTF-8 解码输入。",[53,3285,3286],{},"在平台暴露解码错误时，拒绝编码格式不正确的输入。",[53,3288,3289,3290,3292,3293,2537],{},"用 ",[16,3291,985],{}," 包裹 ",[16,3294,18],{},[53,3296,3297],{},"用 schema 或业务规则校验解析后的值。",[53,3299,3300],{},"避免记录完整的不可信 payload，特别是可能包含敏感信息的内容。",[53,3302,3303,3304,2464,3306,2537],{},"JSON 允许 Unicode 字符串——不要限制为\"可打印 ASCII\"，否则会拒绝合法的国际化数据，如 ",[16,3305,1027],{},[16,3307,1030],{},[12,3309,3310,3311,2537],{},"文本能解析后，语法校验只是第一步。在应用中接受数据之前，用 ",[36,3312,3313],{"href":1036},"JSON Schema 校验来检查必填字段、数据类型和允许值",[89,3315],{},[42,3317,3319],{"id":3318},"排查步骤清单可收藏","排查步骤清单（可收藏）",[12,3321,3322],{},"遇到“JSON 解析失败”时，可以按以下顺序快速排查：",[145,3324,3325,3330,3338,3351,3357,3370,3376,3382],{},[53,3326,3327,2484],{},[25,3328,3329],{},"看状态码",[53,3331,3332,3335,3336,2500],{},[25,3333,3334],{},"看 Content-Type","：是否为 ",[16,3337,175],{},[53,3339,3340,2492,3343],{},[25,3341,3342],{},"看响应内容",[50,3344,3345,3348],{},[53,3346,3347],{},"是纯 JSON，还是 HTML\u002F文本？",[53,3349,3350],{},"是否前后有多余字符？",[53,3352,3353,3356],{},[25,3354,3355],{},"看大小","：是否异常大（几 MB 以上）？",[53,3358,3359,2492,3362],{},[25,3360,3361],{},"用工具校验",[50,3363,3364],{},[53,3365,3366,3367,3369],{},"将响应粘贴到 ",[36,3368,39],{"href":38}," 中，自动检查语法错误。",[53,3371,3372,3375],{},[25,3373,3374],{},"看编码","：是否有 BOM 头或非 UTF-8 编码？",[53,3377,3378,3381],{},[25,3379,3380],{},"看日志","：后端是否有异常堆栈输出到响应中？",[53,3383,3384,2492,3387],{},[25,3385,3386],{},"看代码",[50,3388,3389,3396],{},[53,3390,3391,3393,3394,2500],{},[16,3392,18],{}," 是否有 ",[16,3395,985],{},[53,3397,3398],{},"是否对非 2xx 响应也尝试解析？",[89,3400],{},[42,3402,3404],{"id":3403},"如何预防-json-解析失败","如何预防 JSON 解析失败？",[145,3406,3407,3427,3458,3481],{},[53,3408,3409,3412],{},[25,3410,3411],{},"统一接口规范",[50,3413,3414,3419,3424],{},[53,3415,3416,3417],{},"成功响应：",[16,3418,1146],{},[53,3420,3421,3422],{},"错误响应：",[16,3423,1152],{},[53,3425,3426],{},"所有响应均为纯 JSON，无额外文本。",[53,3428,3429,3432],{},[25,3430,3431],{},"前端统一解析逻辑",[50,3433,3434,3455],{},[53,3435,3436,3437,3439,3440],{},"封装一个 ",[16,3438,1168],{}," 工具函数：\n",[50,3441,3442,3445,3450],{},[53,3443,3444],{},"检查状态码",[53,3446,3447,3448],{},"检查 ",[16,3449,162],{},[53,3451,3452,3453],{},"再 ",[16,3454,18],{},[53,3456,3457],{},"所有接口调用都通过这个函数，减少重复错误。",[53,3459,3460,3463],{},[25,3461,3462],{},"日志与监控",[50,3464,3465,3478],{},[53,3466,3467,3468],{},"对解析失败的请求，记录：\n",[50,3469,3470,3473,3475],{},[53,3471,3472],{},"状态码",[53,3474,162],{},[53,3476,3477],{},"响应前 1KB（脱敏后）",[53,3479,3480],{},"便于后续定位是后端问题还是网络问题。",[53,3482,3483,3486],{},[25,3484,3485],{},"善用工具",[50,3487,3488,3501],{},[53,3489,3490,3491],{},"开发阶段：\n",[50,3492,3493,3498],{},[53,3494,3289,3495,3497],{},[36,3496,39],{"href":38}," 快速校验接口返回的 JSON。",[53,3499,3500],{},"对复杂结构，先格式化再阅读。",[53,3502,3503,3504],{},"生产问题排查：\n",[50,3505,3506],{},[53,3507,3508],{},"将问题样本粘贴到编辑器中，快速定位错误位置。",[89,3510],{},[42,3512,3513],{"id":3513},"小结",[50,3515,3516,3519,3522],{},[53,3517,3518],{},"“JSON 解析失败”背后可能有多种原因：HTML 错误页、格式错误、编码问题、截断、双重序列化等。",[53,3520,3521],{},"通过系统的排查步骤（状态码 → Content-Type → 响应内容 → 工具校验），可以大幅缩短调试时间。",[53,3523,3524],{},"从长远看，统一接口规范、封装解析逻辑、善用校验工具，是减少这类问题的关键。",[30,3526,3527],{},[12,3528,3529,3530,3532],{},"下次遇到 JSON 解析报错时，可以先将响应粘贴到 ",[36,3531,39],{"href":38}," 中，快速确认是否是语法问题，再决定是前端处理还是找后端排查。",[42,3534,3535],{"id":3535},"下一步",[50,3537,3538,3544,3550],{},[53,3539,3540,3541,2537],{},"文本能解析后，语法校验只是第一层。了解如何使用 ",[36,3542,3543],{"href":1036},"JSON Schema 校验来检查数据结构、必填字段和数据类型",[53,3545,3546,3547,2537],{},"如果需要 JSON 结构和语法规则的基础 overview，请参阅 ",[36,3548,3549],{"href":1283},"JSON 是什么？",[53,3551,3552,3553,2537],{},"关于接口设计模式和响应结构的最佳实践，请参阅 ",[36,3554,3555],{"href":1290},"JSON 最佳实践",{"title":372,"searchDepth":1293,"depth":1293,"links":3557},[3558,3559,3565,3572,3578,3584,3590,3596,3602,3608,3614,3621,3622,3623,3624],{"id":2387,"depth":1293,"text":2387},{"id":2431,"depth":1293,"text":2432,"children":3560},[3561,3562,3563,3564],{"id":2435,"depth":1299,"text":2435},{"id":2458,"depth":1299,"text":2458},{"id":2473,"depth":1299,"text":2473},{"id":2515,"depth":1299,"text":2515},{"id":2548,"depth":1293,"text":3566,"children":3567},"场景 2：Content-Type 不是 application\u002Fjson",[3568,3569,3570,3571],{"id":2554,"depth":1299,"text":2435},{"id":2574,"depth":1299,"text":2458},{"id":2593,"depth":1299,"text":2473},{"id":2611,"depth":1299,"text":2515},{"id":2633,"depth":1293,"text":2634,"children":3573},[3574,3575,3576,3577],{"id":2637,"depth":1299,"text":2435},{"id":2662,"depth":1299,"text":2662},{"id":2697,"depth":1299,"text":2473},{"id":2714,"depth":1299,"text":2515},{"id":2727,"depth":1293,"text":2728,"children":3579},[3580,3581,3582,3583],{"id":2731,"depth":1299,"text":2435},{"id":2756,"depth":1299,"text":2458},{"id":2769,"depth":1299,"text":2473},{"id":2806,"depth":1299,"text":2515},{"id":2830,"depth":1293,"text":2831,"children":3585},[3586,3587,3588,3589],{"id":2837,"depth":1299,"text":2435},{"id":2864,"depth":1299,"text":2458},{"id":2878,"depth":1299,"text":2473},{"id":2893,"depth":1299,"text":2515},{"id":2922,"depth":1293,"text":2923,"children":3591},[3592,3593,3594,3595],{"id":2926,"depth":1299,"text":2435},{"id":2940,"depth":1299,"text":2458},{"id":2951,"depth":1299,"text":2473},{"id":2962,"depth":1299,"text":2515},{"id":2981,"depth":1293,"text":2982,"children":3597},[3598,3599,3600,3601],{"id":2985,"depth":1299,"text":2435},{"id":3002,"depth":1299,"text":2458},{"id":3010,"depth":1299,"text":2473},{"id":3038,"depth":1299,"text":2515},{"id":3051,"depth":1293,"text":3052,"children":3603},[3604,3605,3606,3607],{"id":3055,"depth":1299,"text":2435},{"id":3080,"depth":1299,"text":2458},{"id":3091,"depth":1299,"text":2473},{"id":3102,"depth":1299,"text":2515},{"id":3149,"depth":1293,"text":3150,"children":3609},[3610,3611,3612,3613],{"id":3153,"depth":1299,"text":2435},{"id":3174,"depth":1299,"text":2458},{"id":3185,"depth":1299,"text":2473},{"id":3196,"depth":1299,"text":2515},{"id":3212,"depth":1293,"text":3615,"children":3616},"场景 10：用户输入或第三方数据未校验就 JSON.parse",[3617,3618,3619,3620],{"id":3218,"depth":1299,"text":2435},{"id":3231,"depth":1299,"text":2458},{"id":3242,"depth":1299,"text":2473},{"id":3270,"depth":1299,"text":2515},{"id":3318,"depth":1293,"text":3319},{"id":3403,"depth":1293,"text":3404},{"id":3513,"depth":1293,"text":3513},{"id":3535,"depth":1293,"text":3535},"从真实接口调试场景出发，总结 JSON.parse 报错的 10 类典型错误，并给出逐步排查方法与预防建议。","\u002Fblog\u002Fcover\u002Fzh\u002Fjson-parse-error-debug-cover.svg",[1373,1372],{},"\u002Fzh\u002Fblog\u002Fjson-parse-error-debug",{"slug":1378,"text":3631,"btn":3632},"🚀 先检查你的 JSON 是否合法：","打开 JSON Editor",{"title":2361,"description":3625},"zh\u002Fblog\u002Fjson-parse-error-debug",[1384,39,1385,1386,1387,18],"CFBkuhUr3kITWb-_dlpbNPLXa2aPef5JkWhpt0IGWXQ",1791273859301]