{"record":{"id":"dcfa75f635758d11","repo":"binary-husky/gpt_academic","slug":"chunk-decoded","errorCode":null,"errorMessage":"无法读取以下数据，请检查配置。\n\n{chunk_decoded}","messagePattern":"无法读取以下数据，请检查配置。\n\n(.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"request_llms/bridge_chatgpt.py","lineNumber":358,"sourceCode":"                return\n\n            # 提前读取一些信息 （用于判断异常）\n            chunk_decoded, chunkjson, has_choices, choice_valid, has_content, has_role = decode_chunk(chunk)\n\n            if is_head_of_the_stream and (r'\"object\":\"error\"' not in chunk_decoded) and (r\"content\" not in chunk_decoded):\n                # 数据流的第一帧不携带content\n                is_head_of_the_stream = False; continue\n\n            if \"error\" in chunk_decoded: logger.error(f\"接口返回了未知错误: {chunk_decoded}\")\n\n            if chunk:\n                try:\n                    if has_choices and not choice_valid:\n                        # 一些垃圾第三方接口的出现这样的错误\n                        continue\n                    if ('data: [DONE]' not in chunk_decoded) and len(chunk_decoded) > 0 and (chunkjson is None):\n                        # 传递进来一些奇怪的东西\n                        raise ValueError(f'无法读取以下数据，请检查配置。\\n\\n{chunk_decoded}')\n                    # 前者是API2D & One-API的结束条件，后者是OPENAI的结束条件\n                    one_api_terminate = ('data: [DONE]' in chunk_decoded)\n                    openai_terminate = (has_choices) and (len(chunkjson['choices'][0][\"delta\"]) == 0)\n                    if one_api_terminate or openai_terminate:\n                        is_termination_certain = False\n                        if one_api_terminate: is_termination_certain = True # 抓取符合规范的结束条件\n                        elif (has_choices) and (chunkjson['choices'][0].get('finish_reason', 'null') == 'stop'): is_termination_certain = True # 抓取符合规范的结束条件\n                        if is_termination_certain:\n                            reach_termination = True\n                            log_chat(llm_model=llm_kwargs[\"llm_model\"], input_str=inputs, output_str=gpt_replying_buffer)\n                            break # 对于符合规范的接口，这里可以break\n                        else:\n                            continue # 对于不符合规范的接口，这里需要继续\n                    # 到这里，我们已经可以假定必须包含choice了\n                    try:\n                        status_text = f\"finish_reason: {chunkjson['choices'][0].get('finish_reason', 'null')}\"\n                    except:\n                        logger.error(f\"一些第三方接口出现这样的错误，兼容一下吧: {chunk_decoded}\")","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/binary-husky/gpt_academic/blob/d6bde0fa54373309bd05823a49bda8da019d2c77/request_llms/bridge_chatgpt.py#L340-L376","documentation":"ValueError raised inside the predict (streaming UI) path when a received chunk is non-empty, is not the 'data: [DONE]' sentinel, and fails to parse into a chunkjson (chunkjson is None). The bridge expected SSE 'data:' frames but got something unreadable — usually an HTML error page, a proxy/auth interstitial, or a relay heartbeat — and refuses to continue with unparseable data. The offending raw chunk is embedded in the message.","triggerScenarios":"API_URL_REDIRECT pointing at a wrong path returning HTML (404/502 pages); Cloudflare or corporate proxy intercepting the request; the relay returning empty-body keepalives or non-JSON comments mid-stream; chunk did start with 'data:' per outer checks but its JSON payload is corrupt, leaving chunkjson None.","commonSituations":"Misconfigured custom API host (missing /v1/chat/completions suffix); reverse-proxy (nginx) error pages injected into the stream; free relay services with flaky SSE framing; TLS-intercepting corporate proxies mangling chunk boundaries.","solutions":["Check the chunk_decoded text printed in the error — an HTML title (404/502/Cloudflare) immediately identifies the layer that broke.","Fix API_URL_REDIRECT in config_private.py to the exact chat/completions path of your provider.","Bypass corporate proxies / VPN for the API host, or add the proxy exclusion, then retry.","If the relay is flaky, switch to another channel/key or the official endpoint."],"exampleFix":"# before\nAPI_URL_REDIRECT = \"https://my-relay.com\"\n\n# after\nAPI_URL_REDIRECT = \"https://my-relay.com/v1/chat/completions\"","handlingStrategy":"validation","validationCode":"import requests\nr = requests.post(api_url, headers=headers, json={'model': model, 'messages': [{'role':'user','content':'ping'}], 'max_tokens': 1}, stream=True, timeout=10)\nfirst = next(r.iter_lines()).decode(errors='replace')\nassert first.startswith('data:'), f\"endpoint returns non-SSE first frame: {first[:120]}\"","typeGuard":null,"tryCatchPattern":"try:\n    yield from predict(inputs, llm_kwargs, plugin_kwargs, chatbot, history)\nexcept ValueError as e:\n    if '无法读取以下数据' in str(e):\n        report_and_fix_endpoint(str(e))  # HTML body in error => wrong URL or proxy\n    else:\n        raise","preventionTips":["Smoke-test custom API URLs with a one-token streaming request before real use.","Ensure API_URL_REDIRECT ends with the exact /v1/chat/completions style path.","Keep the network path to the API free of HTML-injecting proxies (captive portals, corporate MITM).","Embed the raw failing chunk in bug reports — it names the offending layer instantly."],"tags":["json","streaming","proxy","configuration","relay"],"backgroundTag":null,"analyzedSha":"d6bde0fa54373309bd05823a49bda8da019d2c77","analyzedAt":"2026-08-14T22:48:35.038Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}