JSON Unexpected token — trailing comma, nháy đơn và cách tìm vị trí lỗi
Unexpected token = chuỗi không phải JSON hợp lệ — hay gặp nhất: trailing comma, nháy đơn, comment / key không quote (viết kiểu JS). Đọc position, dán vào JSON formatter để khoanh dòng, sửa ở nguồn (API serializer hoặc file config), rồi gắn check CI. Đừng eval payload “gần giống JSON”.
Stack Overflow có trailing comma. Paste vào Chrome Console — chạy vì đó là object literal. Đưa cùng nội dung vào JSON.parse hoặc config Node trên CI tối thứ Sáu — SyntaxError: Unexpected token ] in JSON at position 847. JSON đang nói: đây là cosplay JavaScript, không phải JSON.
Bài này hướng developer Việt Nam đang debug API response, log staging, file config — cách đọc lỗi, bảng nguyên nhân, workflow thực tế và các bẫy copy-paste từ DevTools.
Ai gặp lỗi này
- Frontend
JSON.parse(await res.text())fail sau khi backend “trông ổn” - Copy JSON từ log Elasticsearch / CloudWatch bị cắt hoặc escape sai
- File
config.json/package-adjacent config sửa tay, thêm comment// - Nhầm VS Code JSONC (cho phép comment) với parser production
Unexpected token là gì
JSON.parse(str) ném SyntaxError khi str vi phạm cú pháp JSON (RFC 8259). Không liên quan schema business (thiếu field) — đó là lớp cú pháp thuần.
JSON không phải object literal JavaScript. Nó là subset chặt hơn:
| Tính năng | JS object literal | JSON |
|---|---|---|
| Trailing comma | Được (ES5+) | Không |
| Comment // hoặc /* */ | Được | Không (JSONC ≠ JSON) |
| Key | Có hoặc không quote | Bắt buộc nháy kép |
| Nháy đơn ' | Được cho string | Không — chỉ " |
| undefined / NaN / Infinity | Được trong JS | Không hợp lệ |
Top nguyên nhân (có ví dụ)
1. Trailing comma — thủ phạm số 1
{
"name": "app",
"version": "1.0.0",
}
Phẩy sau "1.0.0" → thường báo Unexpected token }. Trong mảng:
["a", "b",]
→ thường Unexpected token ].
2. Nháy đơn
{ 'id': 1 } // object literal OK trong JS — JSON FAIL
{ "id": 1 }
Lỗi hay gặp: Unexpected token '.
3. Key không bọc nháy kép
{ age: 25 }
Phải là { "age": 25 }.
4. Comment
{ "data": [] // tạm tắt }
VS Code JSONC tô xanh — JSON.parse vẫn chết. Cần comment → dùng .jsonc + toolchain rõ, hoặc YAML/JSON5 có chủ đích.
5. undefined / NaN
{ "score": NaN }
Invalid. Dùng null hoặc bỏ field. Lỗi kiểu Unexpected token u (bắt đầu của undefined) rất điển hình khi JSON.stringify bị thay bằng concat tay.
6. Control character trong string
Copy stack trace nhiều dòng vào field "message" mà không escape \n → parser gãy giữa chuỗi.
7. Số hex / octal
JSON chỉ số thập phân. 0xFF → lỗi.
Đọc message lỗi như cheat sheet
| Đoạn lỗi | Khả năng cao |
|---|---|
Unexpected token ] | Trailing comma trong mảng |
Unexpected token } | Trailing comma trong object |
Unexpected token ' | Nháy đơn |
Unexpected token u … | undefined |
Unexpected end of JSON input | Chuỗi cắt cụt, thiếu } / ] |
Unexpected token < | Response HTML (login page, 404), không phải JSON |
Position N là offset ký tự (thường tính từ 0). Nhảy tới vị trí đó trong editor, hoặc dùng tool highlight dòng–cột — nhanh hơn đoán bằng mắt trên minified one-liner.
Workflow debug thực tế
- Giữ raw string — log
typeofvà 200 ký tự đầu. Nếu đã là object,console.logra[object Object]thì đừngJSON.parselại. - Dán vào formatter — bắt lỗi cú pháp đầu tiên kèm dòng.
- Đối schema — OpenAPI / Zod / JSON Schema: shape kỳ vọng vs payload thật (sau khi parse được).
- Minimize — file config lớn: xóa nửa dưới, parse lại; binary search đến khối lỗi.
- Sửa nguồn — serializer server (
json.dumps/JSON.stringify/ framework) thay vì regex vá trên client mãi. - CI —
jq . config.jsonhoặcjsonlinttrước deploy.
console.log(obj) in object đã parse. Còn JSON.parse(obj) khi obj không phải string → lỗi khác hoặc hành vi khó đoán. Chỉ parse chuỗi. Nếu fetch đã res.json(), đừng parse lần nữa.
Case study 1 — Config “chạy trên máy tôi”
Dev thêm:
{
"apiUrl": "https://staging.example.com",
"timeoutMs": 5000, // tăng tạm
}
Local: extension JSONC im lặng. Pipeline Node JSON.parse(fs.readFileSync(...)) → Unexpected token /. Fix: bỏ comment và trailing comma; hoặc đổi sang JSON5 loader có document trong README team.
Case study 2 — Log API / NDJSON / SSE
Hỗ trợ khách paste “JSON từ Network tab”:
data: {"ok":true}
data: {"ok":false,"err":"timeout"}
Parse cả block → fail ngay dòng 2 hoặc tại data:. Phải từng dòng, strip prefix data: , rồi JSON.parse từng event.
Tương tự NDJSON (một JSON / dòng): không nhét cả file vào một lần parse.
Khi status 401/502 trả HTML:
<!DOCTYPE html><html>...
→ Unexpected token <. Kiểm tra res.ok và Content-Type: application/json trước.
Phân biệt nhanh trước khi đổ lỗi API
| Kiểm tra | Ý nghĩa |
|---|---|
| Status HTTP | 200 nhưng body HTML vẫn parse fail |
| Content-Type | text/html vs application/json |
Body bắt đầu bằng { / [ | Nếu < → không phải JSON |
| Copy từ chat | Xuống dòng / dấu … cắt giữa string |
| Double-encoded | String JSON nằm trong string — parse hai lần có chủ đích |
Validate trên trình duyệt trước khi ship
Dán payload (đã che token/PII) vào formatter local để bắt trailing comma trước production.
🔧 Định dạng JSON và tìm lỗi ngay tại đây
JSON Formatter Kawa chạy trên trình duyệt — pretty-print, phát hiện lỗi cú pháp, minify khi cần. Phù hợp log nội bộ hơn pastebin lạ.
Checklist PR / CI
- Không comment trong
.jsonproduction - Không trailing comma (bật rule ESLint/
jsonlintnếu team hay quên) - Client chỉ
JSON.parsekhi chắc là string JSON - API error path không trả HTML mà client vẫn
.json()mù quáng - Test fixture có case tiếng Việt trong string JSON (đảm bảo file UTF-8, không BOM lạ nếu toolchain nhạy)
Tóm tắt
- JSON chặt hơn JS — trailing comma & nháy đơn là “bình thường” trong JS nhưng cấm trong JSON
- Đọc token + position trước khi đoán
- Formatter local khoanh dòng nhanh hơn debug mù
- Sửa serializer / file nguồn; thêm CI
Unexpected token <→ thường HTML, không phải “JSON hơi sai”