خطای Unexpected Token در JSON یعنی چه و چطور رفعش کنیم؟

این خطا یعنی متنی که می‌خواستید پارس کنید اصلاً JSON نیست؛ توکنی که در پیام آمده معمولاً دقیقاً می‌گوید به‌جای JSON چه چیزی دریافت کرده‌اید.

همین حالا امتحان کنید: اعتبارسنجی JSON فرمت‌کننده JSON

JSON.parse() یا response.json() را صدا می‌زنید و به‌جای داده، یک SyntaxError تحویل می‌گیرید. متن پیام از مرورگری به مرورگر دیگر فرق می‌کند، اما معنایش همیشه یکی است: پارسر متن را از ابتدا خوانده و به نویسه‌ای رسیده که در یک JSON معتبر نمی‌تواند در آن نقطه بیاید. همان نویسه‌ای که در پیام نقل شده و موقعیتی که گزارش شده، مفیدترین سرنخ‌هایی هستند که برای پیدا کردن علت در اختیار دارید؛ پس پیش از دست زدن به کد، پیام را با دقت بخوانید.

یک خطا با پیام‌های متفاوت در هر موتور

نسخه‌های جدیدتر کروم و Node.js متن این پیام را عوض کرده‌اند، برای همین در نتایج جستجو و در لاگ‌های قدیمی‌تر با هر دو شکل آن روبه‌رو می‌شوید. جدول زیر نشان می‌دهد همین خطا در هر محیط چطور نوشته می‌شود:

کجا می‌بینیدپیام
کروم، اج و Node.js 20 به بعدUnexpected token '<', "<!DOCTYPE "... is not valid JSON
نسخه‌های قدیمی‌تر کروم و Node.js 18Unexpected token < in JSON at position 0
فایرفاکسJSON.parse: unexpected character at line 1 column 1 of the JSON data
سافاریJSON Parse error: Unrecognized token '<'

عبارت «position 0» یا «line 1 column 1» یعنی همان نویسه اول متن هم نادرست بوده است. این تقریباً همیشه نشان می‌دهد که پاسخ از اساس چیزی غیر از JSON بوده، نه اینکه داخل یک سند JSON اشتباه تایپی کوچکی رخ داده باشد. به بیان دیگر، در این حالت دنبال کامای جاافتاده نگردید؛ اول ببینید اصلاً چه چیزی به دستتان رسیده است.

علت معمول: صفحه HTML به‌جای JSON

نویسه غیرمنتظره < در موقعیت صفر، با فاصله زیاد رایج‌ترین شکل این خطاست. کد شما از یک API داده JSON خواسته، اما سرور در جواب یک صفحه HTML فرستاده است و HTML با <!DOCTYPE html> یا <html> شروع می‌شود. دلیل‌های معمول این اتفاق:

  • نشانی اشتباه: یک غلط تایپی یا جا افتادن پیشوند /api باعث می‌شود صفحه ۴۰۴ سایت برگردد.
  • خطای سرور: فریم‌ورک به‌جای داده، یک صفحه خطای ۵۰۰ به شکل HTML می‌سازد.
  • نشست منقضی‌شده: درخواست به صفحه ورود هدایت می‌شود و fetch بی‌سروصدا این ریدایرکت را دنبال می‌کند.
  • مسیر پیش‌فرض اپلیکیشن تک‌صفحه‌ای (SPA): سرور توسعه یا هاست برای هر مسیر ناشناخته، index.html را با کد وضعیت ۲۰۰ برمی‌گرداند.
  • پراکسی، CDN یا فایروال که میان راه قرار دارد و صفحه خطا یا صفحه چالش HTML خودش را می‌فرستد.

سریع‌ترین راه برای مطمئن شدن این است که پیش از پارس کردن، پاسخ خام را ببینید:

const res = await fetch('/api/items');
console.log(res.status, res.headers.get('content-type'));
const text = await res.text();
console.log(text.slice(0, 200)); // shows the HTML if that is what came back

در مرورگر هم می‌توانید پنل Network را در ابزار توسعه‌دهنده باز کنید، روی همان درخواست بزنید و زبانه Response را ببینید. اگر آنجا HTML می‌بینید، مشکل را در نشانی درخواست، احراز هویت یا خطای سرور برطرف کنید؛ خود کدی که پارس می‌کند ایرادی ندارد و تغییر دادنش کمکی نمی‌کند.

توکن‌های دیگر و اینکه به چه اشاره می‌کنند

همه پیام‌ها به HTML ختم نمی‌شوند. نویسه‌ای که در پیام آمده، یا عبارتی که پیش از «is not valid JSON» نقل شده، معمولاً مستقیم به علت اشاره می‌کند:

پیام فعلی (پیام قدیمی‌تر)چیزی که پارس کرده‌ایدراه‌حل
"undefined" is not valid JSON (Unexpected token u in JSON at position 0)مقدار undefined؛ مثلاً کلیدی که در localStorage وجود ندارد یا متغیری که مقدار نگرفته استپیش از پارس کردن، مطمئن شوید مقدار وجود دارد
"[object Object]" is not valid JSON (Unexpected token o in JSON at position 1)شیئی که قبلاً پارس شده و JSON.parse آن را به رشته تبدیل کرده استمستقیم از همان شیء استفاده کنید و دوباره پارسش نکنید
Expected property name or '}' in JSON at position 1تک‌کوتیشن یا کلید بدون کوتیشن، مثل {'a': 1} یا {a: 1}همه کلیدها و رشته‌ها را داخل دابل‌کوتیشن بنویسید
Expected double-quoted property name in JSON at position 8کامای انتهایی، مثل {"a": 1,}کامای پیش از آکولاد بسته را حذف کنید
Unexpected token 'N', "{"a": NaN}" is not valid JSONNaN، Infinity یا undefined داخل دادهآن‌ها را با null یا یک عدد جایگزین کنید
پیامی که در موقعیت صفر به توکنی نامرئی یا به‌ظاهر خالی اشاره می‌کندنشانه ترتیب بایت (BOM) در ابتدای فایلی که با برخی ویرایشگرهای ویندوزی ذخیره شده استفایل را با UTF-8 بدون BOM ذخیره کنید یا نویسه اول را حذف کنید

پارس کردن داده‌ای که از قبل پارس شده

کتابخانه‌ها اغلب شیء آماده تحویلتان می‌دهند. response.json() خودش یک شیء جاوا اسکریپت برمی‌گرداند و axios هم همین کار را در response.data انجام می‌دهد. اگر این شیء را دوباره به JSON.parse بدهید، اول به رشته "[object Object]" تبدیل می‌شود و پارس در موقعیت ۱ شکست می‌خورد، چون بعد از براکت اول نویسه o آمده است. راه‌حل فقط حذف همان پارس اضافه است:

const data = await res.json();
// JSON.parse(data)  <- fails: data is already an object
console.log(data.items);

پیدا کردن اشتباه داخل یک JSON واقعی

وقتی موقعیت خطا جایی در میانه متن است، متن واقعاً JSON است و فقط اشتباهی درونش وجود دارد. متن را در اعتبارسنج JSON بچسبانید: این ابزار از همان پارسر مرورگر شما استفاده می‌کند، پیام خطا را همراه با موقعیت و شماره خط نشان می‌دهد و به‌محض اینکه سند معتبر شد، آن را مرتب و خوانا می‌کند. اشتباه اصلی معمولاً درست پیش از موقعیت گزارش‌شده است؛ مثلاً کامایی که میان دو عضو جا افتاده یا کوتیشنی که باید دابل‌کوتیشن می‌بود.

پیشگیری از این خطا در کد

اگر پیش از پارس کردن، کد وضعیت و نوع محتوای پاسخ را بررسی کنید، یک خطای نحوی گیج‌کننده به پیامی تبدیل می‌شود که صریحاً می‌گوید چه اتفاقی افتاده است. تابع زیر همین کار را انجام می‌دهد و در پیام خطا، کد وضعیت، نوع محتوا و ابتدای بدنه پاسخ را هم می‌آورد:

async function getJson(url) {
  const res = await fetch(url);
  const type = res.headers.get('content-type') || '';
  if (!res.ok || !type.includes('application/json')) {
    const body = await res.text();
    throw new Error(`Expected JSON from ${url}, got ${res.status} ${type}: ${body.slice(0, 100)}`);
  }
  return res.json();
}

همین فکر در سمت سرور هم به کار می‌آید: مسیرهای API باید بدنه خطا را به شکل JSON برگردانند، نه صفحه خطای HTML، و همراه آن هدر Content-Type: application/json را بفرستند. به این ترتیب کلاینت حتی هنگام خطا هم چیزی دریافت می‌کند که می‌تواند بخواند. برای پیام نزدیک به این خطا که وقتی بدنه پاسخ خالی یا نیمه‌کاره است ظاهر می‌شود، راهنمای خطای Unexpected end of JSON input را ببینید.

سوالات متداول

پیام Unexpected token < in JSON at position 0 یعنی چه؟

پاسخ با نویسه < شروع شده، یعنی HTML است و نه JSON؛ معمولاً یک صفحه خطا، صفحه ورود یا index.html پیش‌فرض. نشانی درخواست، کد وضعیت و هدر Content-Type را بررسی کنید.

چرا پیام می‌گوید «[object Object]» is not valid JSON؟

یک شیء را به JSON.parse داده‌اید و این شیء اول به متن [object Object] تبدیل شده است. مقدار از قبل پارس شده، پس مستقیم از خودش استفاده کنید.

آیا اعتبارسنج JSON می‌تواند مشکل را پیدا کند؟

بله، اگر متن قرار است JSON باشد. اعتبارسنج موقعیت دقیق اولین خطا را نشان می‌دهد. اگر متن HTML است، راه‌حل در سرور یا خود درخواست است، نه در JSON.
همین حالا امتحان کنید: اعتبارسنجی JSON فرمت‌کننده JSON