Bir mağazanın ürün listesini, tarayıcının Network panelinde bulduğunuz /api/products?page=1 adresinden, yani sitenin veriyi JSON olarak veren endpoint'inden topluyorsunuz. Betik ilk 300 sayfayı sorunsuz geçiyor, sonra data = r.json() satırında requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0) hatasıyla duruyor. Adres aynı, kod aynı. Araya tek bir print(r.status_code, r.headers.get("Content-Type"), r.text[:200]) satırı ekleyince tablo değişiyor: 403, text/html ve <!DOCTYPE html> ile başlayan bir sayfa. Sunucu JSON yerine bir web sayfası göndermiş, JSON ayrıştırıcısı (parser) da ilk karakterde, < işaretinde vazgeçmiş.
Bu yazıda mesajın ve içindeki konum bilgisinin ne anlama geldiğini, hangi Python kütüphanesinin hangi istisnayı (exception) fırlattığını ve nedeni bulan üç satırlık kontrolü anlatıyoruz. Ardından boş yanıtları, HTML sayfalarını, proxy'nin 407 cevabını ve yalnız JSON'a benzeyen gövdeleri ele alıyor, sonda denenmiş bir parse_json() yardımcı fonksiyonu veriyoruz.
JSONDecodeError: Expecting value ne anlama gelir?
Ayrıştırıcı bir JSON değerinin başlamasını bekliyordu, ama başka bir şeyle karşılaştı. RFC 8259 tanımına göre bir JSON metni tek bir değerdir: bir nesne, bir dizi, çift tırnak içinde bir dize (string), bir sayı, true, false ya da null. Bu yüzden geçerli bir metin, baştaki isteğe bağlı boşluklardan sonra yalnız {, [, ", bir rakam, -, t, f ya da n ile başlayabilir. Ayrıştırıcı bir HTML sayfasının < işaretine, Too Many Requests metninin T harfine ya da boş bir dizenin sonuna rastladığında "Expecting value" diyerek durur.
Bu bir bağlantı hatası değildir. Sunucuya hiç ulaşamayan bir istek daha önce, requests.get() içinde, ConnectionError ya da ProxyError gibi hatalarla düşer (Max Retries Exceeded With URL). JSONDecodeError görüyorsanız bir yanıt gelmiştir; yalnız içeriği JSON olarak okunamamıştır.
"line 1 column 1 (char 0)" size ne söyler?
Sayılar ayrıştırmanın nerede başarısız olduğunu gösterir. json.JSONDecodeError bunları nitelik (attribute) olarak taşır: msg (neden), doc (metnin tamamı), pos (hatanın oluştuğu karakterin sıra numarası), lineno ve colno (Python json dokümantasyonu). char 0 gövdenin ilk karakteridir; yani hiçbir JSON başlamamıştır.
Başka konumlar daha fazlasını söyler:
line 1 column 4 (char 3): gövde yalnız üç boşluktan oluşuyor. Boşluklar atlanır, sonra metin biter.line 2 column 1 (char 1): gövde bir satır sonuyla başlıyor ve ardından JSON olmayan bir şey geliyor; çoğu zaman bir HTML sayfası.- Metnin derinlerinde bir konum: JSON başlamış ama sonradan bozulmuş, örneğin yarıda kesilmiş bir indirmede.
Bir except bloğunun içinde e.doc[:200] bu metnin başını gösterir.
requests, json, httpx ve aiohttp hangi istisnayı fırlatır?
Her kütüphaneyi Python 3.13, Requests 2.34.2, HTTPX 0.28.1 ve AIOHTTP 3.14.3 ile denedik:
- json:
json.loads(),ValueErrorsınıfının bir alt sınıfı olanjson.JSONDecodeErrorfırlatır. - Requests: 2.27.0 sürümünden (Ocak 2022) bu yana
r.json(),requests.exceptions.JSONDecodeErrorfırlatır. Requests değişiklik günlüğüne göre bu sınıf daha önce fırlatılan istisnalardan türer, aynı zamandaRequestExceptionsınıfının da alt sınıfıdır. - simplejson kuruluyken Requests: üst sınıf
simplejson.errors.JSONDecodeErrorolur. Testimizdeexcept json.JSONDecodeErrorbu durumda hatayı kaçırdı;except ValueErroryine yakaladı. - HTTPX:
Response.json()standartjson.decoder.JSONDecodeErrorfırlatır. - AIOHTTP:
await resp.json()önce Content-Type'a bakar ve ayrıştırmaya hiç girmedenContentTypeErrorfırlatır (Attempt to decode JSON with unexpected mimetype: text/html).content_type=Noneverildiğindejson.JSONDecodeErrorfırlatır.
Requests kullanıyorsanız requests.exceptions.JSONDecodeError yakalayın: iki durumda da çalışır. İstemciler arasındaki diğer farkları HTTPX, Requests ve AIOHTTP Karşılaştırması yazısında anlattık.
Nedeni üç satırda nasıl bulursunuz?
Ayrıştırmadan önce gelen yanıtı yazdırın:
print(r.status_code, r.history, r.url)
print(r.headers.get("Content-Type"), len(r.content))
print(r.text[:200])Sonra çıktıyı şu sırayla okuyun:
- Durum kodu ve geçmiş. Durum kodu
2xxmi? Yolda bir301ya da302yönlendirmesi oldu mu? Yönlendirmeden sonrar.status_codeson yanıtın kodunu, yani200gösterir;[<Response [302]>]yalnızr.historyiçinde görünür. - Son URL.
r.url, yönlendirmelerden sonraki adrestir. Adres/loginya da/consentile bitiyorsa API'ye ulaşmamışsınız demektir. - Content-Type. Görmek istediğiniz tür
application/jsonya da+jsonile biten bir türdür. Başka her şey aşağıdaki nedenlerden birine işaret eder. - Uzunluk ve ilk karakterler.
0boş demektir,<HTML,{'metin olarak yazdırılmış bir Python sözlüğü (dict),cb(ise JSONP. - Sonucu aşağıdaki tabloyla eşleştirin.
Log'a yalnız ilk 200 karakteri yazın: gövdenin tamamında token'lar ya da kişisel veriler olabilir.
Gövdenin ilk karakterleri size ne söyler?
Aşağıdaki mesajların hepsi, Python 3.13.9 ve Requests 2.34.2 ile yerel bir test sunucusuna karşı yaptığımız çalıştırmadan geliyor; başka Python sürümleri aynı hatayı farklı sözcüklerle yazabilir.
| Gövdenin başı | Tipik durum kodu ve Content-Type | r.json() mesajı | Olası neden | Ne yapmalı |
|---|---|---|---|---|
| (hiçbir şey) | 204, 304, HEAD ya da boş bir 200 | Expecting value: line 1 column 1 (char 0) | Endpoint içerik döndürmüyor | Ayrıştırmadan önce durum kodunu ve len(r.content) değerini kontrol edin |
<!DOCTYPE html> | 403, 429, 503 ya da bir 302 sonrası 200; text/html | Expecting value: line 1 column 1 (char 0) | Engel sayfası, giriş yönlendirmesi, hata sayfası | Önce durum kodunun nedenini giderin |
<html>...407... ya da hiçbir şey | 407 ve Proxy-Authenticate, http:// hedef | Expecting value: line 1 column 1 (char 0) | Yanlış proxy kimlik bilgisi ya da IP | user:pass bilgisini ve IP whitelist'i kontrol edin |
Too Many Requests | 429, text/plain | Expecting value: line 1 column 1 (char 0) | Düz metin bir hız sınırı cevabı | Yavaşlayın; Retry-After header'ını okuyun |
cb({"items": ...}); | 200, application/javascript | Expecting value: line 1 column 1 (char 0) | JSONP | Endpoint'i callback olmadan kullanın |
{"id":1}, yeni satır, {"id":2} | 200, application/x-ndjson | Extra data: line 2 column 1 (char 9) | NDJSON | Satır satır ayrıştırın |
{'id': 1, ...} | 200, çoğunlukla text/plain | Expecting property name enclosed in double quotes: line 1 column 2 (char 1) | str() ile yazılmış bir dict | Veriyi üreten tarafta json.dumps() kullanın |
Görünmez bir BOM, sonra { | 200, application/json | Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0) | Bayt sırası işareti (BOM) | utf-8-sig ile decode edin |
İlk beş satır aynı mesajı veriyor. Yani mesaj tek başına nedeni hiçbir zaman söylemez; nedeni durum kodu ve Content-Type söyler.
Boş yanıtlar: 204, HEAD ve 304
Bazı cevapların tanım gereği gövdesi yoktur. RFC 9110, 204 No Content yanıtının içerik taşıyamayacağını, 304 Not Modified yanıtında da içerik bulunmadığını ve bir HEAD isteğine gelen cevabın yalnız header'lardan (üst bilgi satırlarından) oluştuğunu söyler. Birçok API DELETE ve PUT isteklerine 204 ile cevap verir: işlem başarılıdır ve ayrıştırılacak bir şey yoktur, ama r.json() yine de Expecting value fırlatır.
Bu cevapları hata değil, "veri yok" olarak ele alın ve ayrıştırmadan önce r.status_code değerine bakın. Boş bir 200 ise başka bir durumdur: genellikle bir sunucu hatasına ya da yanlış bir endpoint'e işaret eder ve log'a yazılmalıdır.
JSON yerine HTML: engel, giriş ve hata sayfaları
JSON beklenen yere üç tür HTML sayfası gelir; hangisinin geldiğini durum kodu söyler.
Engel ya da doğrulama sayfası. text/html ile gelen bir 403, 429 ya da 503 çoğu zaman bir bot korumasının cevabıdır. Onu tanımak için "Just a moment..." ya da "Access denied" gibi <title> metni yeter. Bu sayfayı ayrıştırmayın, döngü içinde yeniden de denemeyin. Her kodun ne anlama geldiğini Scraping'de HTTP Hata Kodları, Cloudflare sayfalarını Cloudflare Scraper, bot puanlamasını da Bot Tespiti Nasıl Yapılır? yazısında anlattık. Meşru yollar resmî bir API, robots.txt kurallarına uyan daha düşük bir istek hızı ya da site sahibinin iznidir.
Yönlendirme sonrası giriş sayfası. Durum kodu 200 olduğu için bu durum kolayca gözden kaçar. r.history içinde [<Response [302]>] görünür ve r.url değeri /login ile biter: oturumunuzun süresi dolmuştur. Kendi hesabınızda çözüm oturum yönetimidir (Python'da Oturum ve Çerez Yönetimi).
Sunucu hata sayfası. HTML ile gelen bir 500, 502 ya da 504, sunucunun kendisinden ya da önündeki bir ağ geçidinden (gateway) gelir: sorun ayrıştırıcınızda değil, API'dedir.
URL, API'yi değil de sayfanın kendisini gösterdiğinde de HTML gelir. JSON, sayfanın yaptığı ayrı bir istekle gelir ve bu isteği Network panelinde bulursunuz (önce API isteğini bulmak).
Proxy üzerinden: 407 cevabı
Düz bir http:// isteğini yanlış şifreyle bir proxy üzerinden gönderdiğinizde 407 Proxy Authentication Required cevabını proxy'nin kendisi verir. Requests bu cevabı, proxy hangi gövdeyi gönderdiyse onunla birlikte normal bir yanıt olarak kodunuza iletir: bir HTML sayfası, kısa bir metin ya da hiçbir şey. Biri HTML, diğeri boş gövde gönderen iki yerel test proxy'siyle denediğimizde r.json() iki seferde de Expecting value verdi.
Hedef https:// ise 407 tünel kurulurken gelir. Bu yüzden requests.get() çağrısı ProxyError: Tunnel connection failed: 407 fırlatır ve r.json() satırına hiç gelinmez (Max Retries Exceeded With URL).
Proxy-Authenticate header'ıyla gelen bir 407 sorunun hedefte değil, proxy'de olduğunu gösterir. Kullanıcı adını ve şifreyi kontrol edin, proxy URL'sindeki özel karakterleri kodlayın (@ işareti %40 olur) ya da IP adresinizin whitelist'te olduğunu doğrulayın (Proxy Kimlik Doğrulama: User:Pass ve IP Whitelist).
JSON'a benzeyen ama JSON olmayan gövdeler: JSONP, NDJSON ve tek tırnak
Bazı gövdeler JSON içerir, ama tek ve temiz bir değer olarak değil.
JSONP
JSONP, JSON'u bir fonksiyon çağrısının içine sarar: cb({"items": [1]});. Genellikle application/javascript türüyle gönderilir. Ayrıştırıcı c harfini görür ve char 0 konumunda Expecting value bildirir. Endpoint'i callback parametresi olmadan kullanın ya da ilk ( ile son ) arasındaki metni alın.
NDJSON ve JSON Lines
Dışa aktarma (export) ve akış (streaming) endpoint'leri çoğu zaman her satıra bir JSON değeri yazar; bu biçim jsonlines.org adresinde tanımlanır. İlk satır ayrıştırılır, sonra ayrıştırıcı arkasında başka metin bulur ve Extra data: line 2 column 1 diyerek durur. Böyle gövdeleri r.iter_lines() ile satır satır okuyun.
Tek tırnak ve Python değerleri
{'id': 1} gibi bir gövde json.dumps() yerine Python'un str() fonksiyonuyla yazılmıştır; ayrıştırıcı char 1 konumunda Expecting property name enclosed in double quotes diyerek durur. Python yazımıyla None ya da True olan bir gövde de Expecting value ile düşer, çünkü JSON bunları null ve true diye yazar. Veriyi yazan kodu düzeltin: text.replace("'", '"') kesme işareti içeren her değeri bozar, ast.literal_eval() ise yalnız kendi ürettiğiniz veride güvenlidir.
Gövdenin başında bir bayt sırası işareti (BOM) varsa Unexpected UTF-8 BOM alırsınız; kodlama tarafını Python Türkçe Karakter Sorunu yazısında anlattık.
Tam örnek: gövdeyi kontrol edip JSON okuyan parse_json()
Bu yardımcı fonksiyon üç satırlık kontrolü koda döker. Ayrıştırılmış veriyi döndürür, tanım gereği gövdesi olmayan cevaplarda None verir ya da durum kodunu, yönlendirmeleri, Content-Type'ı, son URL'yi ve gövdenin başını yazan tek bir NotJSON hatası fırlatır. Requests'i pip install requests ile kurun.
"""JSON yanıtını okur ya da gövdenin neden JSON olmadığını tek satırda söyler."""
import json
import requests
PROXY = "http://user:pass@pr.proxynet.io:8000"
PROXIES = {"http": PROXY, "https": PROXY}
class NotJSON(ValueError):
"""Sunucu cevap verdi, ama istediğimiz JSON ile değil."""
def describe(r):
"""Durum kodu, yönlendirmeler, Content-Type, son URL ve ilk 200 karakter."""
hops = "".join(f"{h.status_code} -> " for h in r.history)
ctype = r.headers.get("Content-Type", "none")
start = r.text[:200].replace("\n", " ")
return f"HTTP {hops}{r.status_code}, {ctype}, {r.url}, body {start!r}"
def media_type(r):
return r.headers.get("Content-Type", "").split(";")[0].strip().lower()
def is_json_type(mtype):
return mtype == "application/json" or mtype.endswith("+json")
def parse_json(r):
"""Ayrıştırılmış gövdeyi döndürür, "içerik yok" için None verir ya da nedeniyle NotJSON fırlatır."""
if r.status_code in (204, 304) or r.request.method == "HEAD":
return None # bu cevaplar tanım gereği gövde taşımaz
mtype = media_type(r)
if not r.ok and not is_json_type(mtype):
raise NotJSON(f"error response, not JSON: {describe(r)}")
if not r.content:
raise NotJSON(f"empty body: {describe(r)}")
if mtype == "application/x-ndjson":
return [json.loads(line) for line in r.iter_lines() if line.strip()]
if r.text.lstrip().startswith("<"):
raise NotJSON(f"HTML instead of JSON: {describe(r)}")
try:
return r.json()
except requests.exceptions.JSONDecodeError as e:
raise NotJSON(f"{e.msg} at char {e.pos}: {describe(r)}") from e
if __name__ == "__main__":
url = "https://example.com/api/products?page=1"
r = requests.get(url, proxies=PROXIES, timeout=(5, 30))
try:
data = parse_json(r)
except NotJSON as e:
print("stop:", e)
else:
if not r.ok:
print("API error:", r.status_code, data)
elif data is None:
print("no content")
else:
print("ok:", type(data).__name__, len(data))Hata durum koduyla gelen ve JSON olmayan bir gövde en başta durdurulur; böylece bir 403 sayfası ya da proxy'nin 407 cevabı hiç ayrıştırılmaz. Hata durum koduyla gelen ama gövdesi JSON olan bir yanıt, örneğin {"error": ...} içeren bir 400, döndürülür, çünkü birçok API hatalarını bu şekilde açıklar; r.ok kontrolünü fonksiyonu çağıran taraf yapar. PROXIES isteğe bağlıdır; timeout=(5, 30) bağlantı için 5, cevap için 30 saniye tanır.
Yardımcı fonksiyon bilerek yeniden deneme yapmaz, IP döndürmez ve paralel çalışmaz. Hangi durum kodlarının yeniden denenmeye değdiğini Scraping'de HTTP Hata Kodları yazısında, IP döndürmeyi Python'da Proxy'ler Nasıl Döndürülür? rehberinde, paralel istekleri de Concurrency ve Parallelism yazısında anlattık.
Çıktı nasıl görünüyor?
parse_json() fonksiyonunu, her yola tablodaki gövdelerden biriyle cevap veren yerel bir test sunucusuna karşı çalıştırdık; son satır, HTML ile 407 cevabı veren bir test proxy'sinden geçti.
/api/products -> {'items': [1, 2, 3]}
/api/items/7 -> None
/api/empty -> NotJSON: empty body: HTTP 200, application/json, http://127.0.0.1:8111/api/empty, body ''
/api/blocked -> NotJSON: error response, not JSON: HTTP 403, text/html; charset=utf-8, http://127.0.0.1:8111/api/blocked, body '<!DOCTYPE html><html><head><title>Just a moment...</title></head></html>'
/api/private -> NotJSON: HTML instead of JSON: HTTP 302 -> 200, text/html; charset=utf-8, http://127.0.0.1:8111/login, body '<!DOCTYPE html> <html><head><title>Sign in</title></head></html>'
/api/slow -> NotJSON: error response, not JSON: HTTP 429, text/plain, http://127.0.0.1:8111/api/slow, body 'Too Many Requests'
/api/jsonp -> NotJSON: Expecting value at char 0: HTTP 200, application/javascript, http://127.0.0.1:8111/api/jsonp, body 'cb({"items": [1]});'
/api/export -> [{'id': 1}, {'id': 2}]
/api/dict -> NotJSON: Expecting property name enclosed in double quotes at char 1: HTTP 200, text/plain, http://127.0.0.1:8111/api/dict, body "{'id': 1, 'name': 'Lamp'}"
/api/bad-request -> {'error': 'page must be a number'} (status 400)
proxy, wrong password -> NotJSON: error response, not JSON: HTTP 407, text/html, http://example.com/api/products, body '<html><head><title>407 Proxy Authentication Required</title></head><body><h1>407</h1></body></html>'/api/items/7 yolu 204 ile cevap verdi, /api/export ise NDJSON gönderdi; ikisi de hata değil. /api/private satırı, yalnız durum koduna bakan bir kontrolün kaçırdığı yönlendirmeyi gösteriyor: 302 -> 200, sonunda da /login.
Kullanım alanları: JSON bekleyen hangi betiklerde çıkar?
- Bir sitenin kendi API'sini çağırmak: Network panelinden kopyaladığınız istek, arkasındaki oturumun ya da token'ın süresi dolunca çalışmaz olur (statik ve dinamik sayfalar).
- Fiyat takibi: ürün JSON'unu okuyan günlük bir iş, fazla hızlı çalıştığı gün bir engel sayfası alır (rakip fiyat takibi).
- API sayfalama: son sayfadan sonraki sayfa boş bir liste yerine
204ya da boş bir gövde döndürebilir (scraping'de sayfalama). - Otomasyon araçları: JSON bekleyen bir n8n HTTP Request düğümü (node) HTML bir hata sayfası alır (n8n proxy ayarları).
- Veri hatları (pipeline): binlerce JSON cevabının arasındaki tek bir HTML cevabı veritabanına yazılmamalı, o toplu işi durdurmalı (veri kazıma).
- Crawler'lar: birçok host'taki JSON endpoint'lerini okuyan bir crawler, başarısız olan her host için tek satırlık açık bir mesaj yazmalıdır (web crawler).
Sık yapılan hatalar
- Hatayı yutmak.
except JSONDecodeError: passhiçbir şey kaydetmez ve nedeni gizler. - 200 durum koduna güvenmek. Bir
302sonrasında gelen giriş sayfası da200olarak gelir.r.historyver.urldeğerlerine bakın. - Tek tırnakları
replace()ile çift tırnağa çevirmek. Kesme işareti içeren her değeri bozar. - Yanıt gövdesine
eval()uygulamak. Sunucu hangi kodu gönderdiyse onu çalıştırır. - Engel sayfasını aynı hızda yeniden denemek. Aynı hızda gönderilen aynı istekler aynı
429ya da403cevabını alır; önce hızı düşürün (429 Too Many Requests). - Gövdenin tamamını log'a yazmak. Nedeni ilk 200 karakter söyler; geri kalanında token'lar ve kişisel veriler olabilir.
get()vejson()çağrılarını tek birexcept RequestExceptionile sarmak. 2.27.0 sürümünden bu yana bu blok ikisini de yakalar; ağ hatası ile ayrıştırma hatası aynı görünür.- Hatayı json modülünde aramak. Ayrıştırıcı doğru çalışıyor; gövde JSON değil.
Karar rehberi
| Ne görüyorsunuz | Ne yapmalı |
|---|---|
Durum kodu 204 ya da gövde boş | r.json() çağırmayın; "veri yok" olarak ele alın |
text/html ile 403, 429 ya da 503 | Ayrıştırmayı bırakın ve durum kodunun nedenini giderin (Scraping'de HTTP Hata Kodları) |
200, ama r.history bir giriş sayfasına 302 gösteriyor | Oturumunuzu yenileyin (Python'da Oturum ve Çerez Yönetimi) |
HTML ya da boş gövdeyle gelen 407 | Proxy kimlik bilgilerini ve IP whitelist'i kontrol edin |
ProxyError: Tunnel connection failed: 407 | https:// hedeflerde aynı neden (Max Retries Exceeded With URL) |
Extra data | r.iter_lines() ile satır satır ayrıştırın |
Gövde callback( ile başlıyor | Endpoint'i callback olmadan kullanın ya da sarmalayıcıyı çıkarın |
Unexpected UTF-8 BOM | utf-8-sig ile decode edin (Python Türkçe Karakter Sorunu) |
Sıkça sorulan sorular
Durum kodu 200 iken r.json() neden hata veriyor?
200 gövdenin biçimi hakkında hiçbir şey söylemez. Yönlendirme sonrası gelen bir giriş sayfası, bir JSONP cevabı ya da boş bir gövde 200 ile gelebilir. Content-Type'a, r.history değerine ve r.text metninin başına bakın.
requests.exceptions.JSONDecodeError ile json.JSONDecodeError arasındaki fark nedir?
r.json(), Requests 2.27.0'dan bu yana requests.exceptions.JSONDecodeError fırlatır. Bu sınıf hem JSON kütüphanesinin JSONDecodeError sınıfının hem de Requests'in kendi RequestException sınıfının alt sınıfıdır; bu yüzden ikisi de onu yakalar. Tek istisna simplejson'dır: kuruluysa except json.JSONDecodeError hatayı kaçırır, bu yüzden Requests'in sınıfını yakalayın.
JSONDecodeError: Extra data neden çıkar?
Ayrıştırıcı tam bir JSON değeri okudu, sonra arkasında başka metin buldu: genellikle NDJSON ya da art arda yazılmış iki nesne. Satır satır ayrıştırın ya da değerleri tek tek okumak için json.JSONDecoder().raw_decode() kullanın.
"Expecting property name enclosed in double quotes" ne demek?
Bir nesnenin içindeki anahtar çift tırnak içinde değil demektir. Genellikle nedeni, str() ile yazılmış ve tek tırnak kullanan bir Python dict'idir. Python 3.12 ve öncesinde } işaretinden önce kalan fazladan bir virgül de bu mesajı verir; Python 3.13 aynı durumu Illegal trailing comma before end of object diye bildirir.
Bu hatayı yfinance ya da spotdl kullanırken alıyorum. Ne yapmalıyım?
Kütüphane uzaktaki bir servisten JSON istemiş ve başka bir şey almıştır. Kütüphaneyi güncelleyin, daha seyrek çağırın ve projenin issue sayfasında aynı mesajı arayın.
Aynı hata JavaScript'te nasıl görünür?
Node.js 24'te JSON.parse() bir HTML sayfasında SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON, boş bir dizede ise SyntaxError: Unexpected end of JSON input fırlatır. Orada da response.json() çağırmadan önce durum kodunu ve Content-Type'ı kontrol edin (JavaScript'te cURL). WordPress editöründeki "The response is not a valid JSON response" uyarısı ise başka bir sorundur.
Özet
JSONDecodeError: Expecting value bir belirtidir, neden değildir. Bağlantı kurulmuş ve sunucu cevap vermiştir, ama gövde boştur ya da JSON değildir. Nedeni üç kontrolle bulursunuz: r.history ile birlikte durum kodu, Content-Type ve gövdenin ilk 200 karakteri. Boş bir 204 normaldir; HTML sayfası engel, giriş ya da hata sayfası demektir; 407 proxy'yi gösterir; Extra data ya da tek tırnak ise gövdenin yalnız JSON'a benzediğini söyler. JSON endpoint'lerini proxy üzerinden okuyan işler için kullanabileceğiniz proxy türlerini proxy hizmetlerimiz sayfasında bulabilirsiniz.




