JSONDecodeError: Expecting Value Hatası Nasıl Çözülür?

Yayın tarihi:

14 dk okuma

Acar Diveroli
Yazar: Acar Diveroli
İnce r.json() kapısının solunda boş, <!DOCTYPE html> ve 407 gövdeli kırmızı kartlar; sağda {"ok": true} kartı geçiyor

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(), ValueError sınıfının bir alt sınıfı olan json.JSONDecodeError fırlatır.
  • Requests: 2.27.0 sürümünden (Ocak 2022) bu yana r.json(), requests.exceptions.JSONDecodeError fırlatır. Requests değişiklik günlüğüne göre bu sınıf daha önce fırlatılan istisnalardan türer, aynı zamanda RequestException sınıfının da alt sınıfıdır.
  • simplejson kuruluyken Requests: üst sınıf simplejson.errors.JSONDecodeError olur. Testimizde except json.JSONDecodeError bu durumda hatayı kaçırdı; except ValueError yine yakaladı.
  • HTTPX: Response.json() standart json.decoder.JSONDecodeError fırlatır.
  • AIOHTTP: await resp.json() önce Content-Type'a bakar ve ayrıştırmaya hiç girmeden ContentTypeError fırlatır (Attempt to decode JSON with unexpected mimetype: text/html). content_type=None verildiğinde json.JSONDecodeError fı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:

python
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:

  1. Durum kodu ve geçmiş. Durum kodu 2xx mi? Yolda bir 301 ya da 302 yönlendirmesi oldu mu? Yönlendirmeden sonra r.status_code son yanıtın kodunu, yani 200 gösterir; [<Response [302]>] yalnız r.history içinde görünür.
  2. Son URL. r.url, yönlendirmelerden sonraki adrestir. Adres /login ya da /consent ile bitiyorsa API'ye ulaşmamışsınız demektir.
  3. Content-Type. Görmek istediğiniz tür application/json ya da +json ile biten bir türdür. Başka her şey aşağıdaki nedenlerden birine işaret eder.
  4. Uzunluk ve ilk karakterler. 0 boş demektir, < HTML, {' metin olarak yazdırılmış bir Python sözlüğü (dict), cb( ise JSONP.
  5. 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-Typer.json() mesajıOlası nedenNe yapmalı
(hiçbir şey)204, 304, HEAD ya da boş bir 200Expecting value: line 1 column 1 (char 0)Endpoint içerik döndürmüyorAyrış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/htmlExpecting 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 şey407 ve Proxy-Authenticate, http:// hedefExpecting value: line 1 column 1 (char 0)Yanlış proxy kimlik bilgisi ya da IPuser:pass bilgisini ve IP whitelist'i kontrol edin
Too Many Requests429, text/plainExpecting 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/javascriptExpecting value: line 1 column 1 (char 0)JSONPEndpoint'i callback olmadan kullanın
{"id":1}, yeni satır, {"id":2}200, application/x-ndjsonExtra data: line 2 column 1 (char 9)NDJSONSatır satır ayrıştırın
{'id': 1, ...}200, çoğunlukla text/plainExpecting property name enclosed in double quotes: line 1 column 2 (char 1)str() ile yazılmış bir dictVeriyi üreten tarafta json.dumps() kullanın
Görünmez bir BOM, sonra {200, application/jsonUnexpected 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.

python
"""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.

text
/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 204 ya 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: pass hiçbir şey kaydetmez ve nedeni gizler.
  • 200 durum koduna güvenmek. Bir 302 sonrasında gelen giriş sayfası da 200 olarak gelir. r.history ve r.url değ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ı 429 ya da 403 cevabı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() ve json() çağrılarını tek bir except RequestException ile 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üyorsunuzNe 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 503Ayrış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österiyorOturumunuzu yenileyin (Python'da Oturum ve Çerez Yönetimi)
HTML ya da boş gövdeyle gelen 407Proxy kimlik bilgilerini ve IP whitelist'i kontrol edin
ProxyError: Tunnel connection failed: 407https:// hedeflerde aynı neden (Max Retries Exceeded With URL)
Extra datar.iter_lines() ile satır satır ayrıştırın
Gövde callback( ile başlıyorEndpoint'i callback olmadan kullanın ya da sarmalayıcıyı çıkarın
Unexpected UTF-8 BOMutf-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.

ChatGPT'ye sorClaude'a sor