Portkey-AI gateway 的一次“假壓縮頭”翻車的完整排障記:由 httpx 解壓異常引發的根因分析

筆者最近在本地搭建了Portkey AI Gateway(模型路由網關),然后按照文檔中的方式進行測試。

在這里插入圖片描述
結果發現,網關能夠接收到請求,但是Python測試的程序卻運行報錯。

在這里插入圖片描述
Python代碼報錯信息如下:

Traceback (most recent call last):File "F:\pytorch310\lib\site-packages\httpx\_decoders.py", line 97, in decodereturn self.decompressor.decompress(data)
zlib.error: Error -3 while decompressing data: incorrect header checkThe above exception was the direct cause of the following exception:Traceback (most recent call last):File "F:\pytorch310\lib\site-packages\portkey_ai\_vendor\openai\_base_client.py", line 989, in requestresponse = self._client.send(File "F:\pytorch310\lib\site-packages\httpx\_client.py", line 928, in sendraise excFile "F:\pytorch310\lib\site-packages\httpx\_client.py", line 922, in sendresponse.read()File "F:\pytorch310\lib\site-packages\httpx\_models.py", line 881, in readself._content = b"".join(self.iter_bytes())File "F:\pytorch310\lib\site-packages\httpx\_models.py", line 898, in iter_bytesdecoded = decoder.decode(raw_bytes)File "F:\pytorch310\lib\site-packages\httpx\_decoders.py", line 99, in decoderaise DecodingError(str(exc)) from exc
httpx.DecodingError: Error -3 while decompressing data: incorrect header checkThe above exception was the direct cause of the following exception:Traceback (most recent call last):File "E:\PycharmProjects\PortkeyAIProject\test.py", line 10, in <module>response = client.chat.completions.create(File "F:\pytorch310\lib\site-packages\portkey_ai\api_resources\apis\chat_complete.py", line 183, in createreturn self.normal_create(File "F:\pytorch310\lib\site-packages\portkey_ai\api_resources\apis\chat_complete.py", line 126, in normal_createresponse = self.openai_client.with_raw_response.chat.completions.create(File "F:\pytorch310\lib\site-packages\portkey_ai\_vendor\openai\_legacy_response.py", line 364, in wrappedreturn cast(LegacyAPIResponse[R], func(*args, **kwargs))File "F:\pytorch310\lib\site-packages\portkey_ai\_vendor\openai\_utils\_utils.py", line 287, in wrapperreturn func(*args, **kwargs)File "F:\pytorch310\lib\site-packages\portkey_ai\_vendor\openai\resources\chat\completions\completions.py", line 925, in createreturn self._post(File "F:\pytorch310\lib\site-packages\portkey_ai\_vendor\openai\_base_client.py", line 1259, in postreturn cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))File "F:\pytorch310\lib\site-packages\portkey_ai\_vendor\openai\_base_client.py", line 1021, in requestraise APIConnectionError(request=request) from err
openai.APIConnectionError: Connection error.

然后筆者定位到報錯位置,發現是gzip解碼時出現了問題。

在這里插入圖片描述

癥狀是 incorrect header check,最終定位到網關在響應里帶了 Content-Encoding: gzip 頭,但實體其實是明文 JSON(即“假壓縮頭”)。任何會自動解壓的客戶端(httpx/Portkey SDK 等)都會在讀取階段失敗。

要想在根源上修復這個問題,需要在網關/反代上保證“頭與實體一致”——不壓就刪 Content-Encoding;壓了就只壓一層并設置正確的頭。可是這個網關是直接運行起來的開源項目,所以筆者只好想了一個臨時的辦法來解決這個問題:客戶端用 httpx.stream(...).iter_raw() 讀原始字節并手動解析。

復現與第一輪嘗試

1) 最初用 Portkey SDK 直連 provider

from portkey_ai import Portkey
client = Portkey(provider="dashscope", Authorization="Bearer <key>")
client.chat.completions.create(model="qwen-plus", messages=[...])

現象httpx.DecodingError
直覺:解壓階段掛了 → 看壓縮相關頭部。

2) 改走本地網關 base_url

Portkey(base_url="http://localhost:8787/v1")

這時 Portkey 的 provider= 參數不會自動變成網關能識別的路由頭,于是網關返回:

400 {'status': 'failure', 'message': 'Either x-portkey-config or x-portkey-provider header is required'}

結論:走自托管網關必須用請求頭指明路由(x-portkey-provider)。

我們補上頭之后,仍舊遇到 DecodingError。說明不僅是路由問題,壓縮/頭部也有坑


關鍵破局:跳過自動解壓,直看“線上的原始字節”

為排除客戶端自動行為干擾,直接用 httpx.stream(...).iter_raw()

import httpx, jsonGATEWAY = "http://localhost:8787/v1"
payload = {"model": "qwen-plus", "messages": [{"role": "user", "content": "Hello"}]}
headers = {"x-portkey-provider": "dashscope","Authorization": "Bearer <your_secret_key>","Accept-Encoding": "identity","Content-Type": "application/json",
}with httpx.stream("POST", f"{GATEWAY}/chat/completions", json=payload, headers=headers, timeout=30) as r:print("status =", r.status_code)print("Content-Encoding =", r.headers.get("content-encoding"))raw = b"".join(r.iter_raw())  # ★ 不做解壓,拿“線上的原始字節”print("raw len =", len(raw))print("raw head =", raw[:80])# 如果服務端其實是明文 JSON(卻錯誤地帶了 Content-Encoding)try:print("as text ->", raw[:200].decode("utf-8"))except Exception:  # 如果真的是 gzip,可自行解壓看import gzip, iotry:txt = gzip.GzipFile(fileobj=io.BytesIO(raw)).read().decode("utf-8")print("as gzip json ->", txt[:200])except Exception as e:print("manual decode failed:", e)

實際輸出(核心證據)

status = 200
Content-Encoding = gzip
raw len = 448
raw head = b'{"choices":[{"message":{"role":"assistant","content":"Hi there! \xd9\xa9(\xe2\x97\x95\xe2\x80\xbf\xe2\x97\x95\xef\xbd\xa1)'
as text -> {"choices":[{"message":{"role":"assistant","content":"Hi there! ?(???。)? How can I assist you today?"},"finish_reason":"stop","index":0,"logprobs":null}],"object":"chat.completion","usage":{

結論:服務端響應頭聲稱 gzip,但實體實際上是明文 JSON
這就是“假壓縮頭”。httpx/SDK 看到 Content-Encoding: gzip 會嘗試解壓,結果自然報 incorrect header check


為何會出現“假壓縮頭”?

常見幾種錯誤鏈路:

  1. 上游原本 gzip,但中間層解壓了實體,卻忘了刪 Content-Encoding
  2. 上游明文,但中間層“不小心”加了 Content-Encoding: gzip
  3. 雙重壓縮:上游已 gzip,網關又對同一實體再壓一層(頭與實體層級不一致)。

這類錯誤必須在服務端/網關側修。只靠客戶端改代碼是治標不治本。

Checklist:以后再遇到類似“解壓報錯”,按這個查

  • 對齊兩個關鍵頭Accept-Encoding(請求) vs Content-Encoding(響應)
  • 抓原始字節iter_raw() → 看是否“明文 + gzip 頭”
  • 斷開自動解壓的假象:客戶端臨時改為手動解析
  • 修服務器:不壓就刪頭;壓就只壓一層、頭與體一致
  • Portkey 自托管:走 base_url 必帶 x-portkey-* 路由頭;鑒權不要留空

到這里,問題的根因就找到了,并且我們也提出了臨時的解決方案。下一步就是深入源碼來看看這到底是怎么個事兒!

讀者朋友們感興趣的話,也可以閱讀下這個項目的源碼(https://github.com/Portkey-AI/gateway),看看它到底是怎么實現的hh~。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/93438.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/93438.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/93438.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

什么是Session? PHP編程中Session用法詳解

一、Session的基本概念 Session 是 Web 開發中用于在服務器端存儲用戶臨時數據的一種機制&#xff0c;它允許服務器在不同的 HTTP 請求之間識別和跟蹤特定用戶的狀態&#xff0c;本質上是?服務器為每個用戶開辟的臨時私有存儲空間?。由于 HTTP 協議本身是無狀態的&#xff…

【大模型】AI平臺 joyagent 2.0 的部署與測試

github鏈接&#xff1a;https://github.com/jd-opensource/joyagent-jdgenie 本篇博客記錄下自己在配置joyagent的過程&#xff0c;以【手動初始化環境&#xff0c;啟動服務】為例&#xff0c;后端調用的deepseek-chat大模型。 前言 JoyAgent是由京東云開源的企業級多智能體系統…

計算機視覺(一):nvidia與cuda介紹

背景與意義 計算機視覺 (Computer Vision, CV) 需要對圖像和視頻進行處理、特征提取和模型訓練&#xff0c;計算量巨大。GPU (圖形處理單元) 擅長并行計算&#xff0c;非常適合深度學習、卷積操作、矩陣乘法等場景。NVIDIA 作為 GPU 領域的領導者&#xff0c;推出了 CUDA (Comp…

阿里云杭州 AI 產品法務崗位信息分享(2025 年 8 月)

&#xff08;注&#xff1a;本崗位信息已獲jobleap.cn授權&#xff0c;可在 CSDN 平臺發布&#xff09; 一、基本信息 招聘方&#xff1a;阿里云工作地點&#xff1a;杭州信息收錄時間&#xff1a;2025 年 08 月 14 日 二、職位主要職責 為 AI 相關產品全流程提供法務支持&…

醫療智慧大屏系統 - Flask + Vue實現

下面我將實現一個完整的醫療智慧大屏系統&#xff0c;使用Flask作為后端框架&#xff0c;前端使用Vue.js結合ECharts進行醫療數據的可視化展示&#xff0c;文章末尾提交源碼下載。 系統設計思路 前端部分&#xff1a; 使用Vue.js構建響應式界面 使用ECharts實現各類醫療數據可…

庫制作與原理(下)

庫制作與原理 (下) 1. 目標文件 編譯和鏈接這兩個步驟&#xff0c;在 Windows 下被我們的 IDE 封裝的很完美&#xff0c;我們一般都是一鍵構建非常方便&#xff0c;但一旦遇到錯誤的時候呢&#xff0c;尤其是鏈接相關的錯誤&#xff0c;很多人就束手無策了。在 Linux 下&#x…

STL 容器

STL是C的核心組成部分&#xff0c;其主要包括了容器、迭代器、算法三大組件。 其中容器負責存儲數據&#xff0c;迭代器是容器和算法的橋梁&#xff0c;負責對容器中的元素進行操作。本文重點介紹容器部分內容。 STL主要容器 STL容器根據特性進行分類&#xff0c;可以分為序列式…

微信小程序 拖拽簽章

微信小程序 拖拽簽章 效果 主要實現的功能點 文件按比例加載圖片(寬高設定拖拽范圍) 彈層展示印章模板 模板拖拽到文件圖片上 實時獲取拽拽位置 難點 彈層中的元素如何拖拽到文件圖片上 實現歷程 版本1.0 以前我們拖拽一個圖層到另一個圖層上,pc端使用的是mousedown mou…

人工智能加速計算套件

按照甲方要求的技術指標的人工智能加速計算套件1套。每套包含以下內容&#xff1a; 1、顯卡 不低于6542Y&#xff1b;容量不低于 48GB GDDR6顯存&#xff1b;CUDA核心不低于14080 個 &#xff1b;第四代Tensor Core不低于440 個&#xff1b;單精度性能不低于69.3 TFLOPS&#x…

端到端測試:復雜系統的終極體檢術

當你的應用像多米諾骨牌一樣牽一發而動全身&#xff0c;如何確保用戶一路暢通無阻&#xff1f;一、為什么我們需要端到端測試&#xff1f; 想象一下&#xff1a;你精心開發的電商應用&#xff0c;用戶登錄順利&#xff0c;商品瀏覽流暢&#xff0c;卻在最后支付時卡殼——原因是…

Perf使用詳解

Perf 工具深度解析 Perf&#xff08;Performance Counters for Linux&#xff09;是 Linux 系統的性能分析工具&#xff0c;基于內核的 perf_event 子系統&#xff0c;通過硬件性能計數器&#xff08;PMC&#xff09;、軟件事件和跟蹤點&#xff08;tracepoints&#xff09;實現…

Windchill 11 Enumerated Type Customization Utility-枚舉類型自定義實用程序

一、Enumerated Type Customization Utility 枚舉類型自定義實用程序&#xff0c;可用于添加或編輯枚舉類型的值&#xff0c;在Windchill 12.0中可直接在類型和屬性管理中編輯&#xff0c;如下圖所示&#xff0c;而在Windchill 11.0中只能通過windchill shell啟動程序&#xff…

git疑問,暫時記錄

有時候把dev本地分支搞亂了,多出幾個提交,好像在遠程倉庫,rebase dev到本地dev,就恢復了,然后再把我開發分支合并過去就ok,就不會多出幾個重復的提交 在自己分支開發提交數據后,不push到遠程倉庫 然后合并到dev分支,推dev分支到遠程倉庫然后在自己分支,rebase到自己分支,然后再…

Java 大視界 -- 基于 Java 的大數據分布式計算在氣象災害預警與應急響應中的應用

Java 大視界 -- 基于 Java 的大數據分布式計算在氣象災害預警與應急響應中的應用引言&#xff1a;Java 筑起氣象防災減災的數字長城正文&#xff1a;Java 構建的氣象智慧防御體系一、氣象大數據的 Java 基座&#xff1a;從采集到存儲的全鏈路優化1.1 多源異構數據的實時匯聚1.2…

MySQL黑盒子研究工具 strace

strace是什么&#xff1f; 按照 strace 官網的描述, strace 是一個可用于診斷、調試和教學的 Linux 用戶空間跟蹤器。我們用它來監控用戶空間進程和內核的交互&#xff0c;比如系統調用、信號傳遞、進程狀態變更等。 strace 底層使用內核的 ptrace 特性來實現其功能。 strace能…

【運維進階】實施任務控制

實施任務控制 在 Ansible 中&#xff0c;“實施任務控制” 通常指的是對任務執行流程的控制&#xff0c;比如&#xff1a; 條件執行&#xff08;when&#xff09; 循環執行&#xff08;with_items / loop&#xff09; 錯誤處理&#xff08;block / rescue / ignore_errors&…

Java 中的線程中斷詳解

Java 中的線程中斷1、什么是線程中斷2、如何觸發線程中斷3、如何處理線程中斷3.1 線程中斷相關的核心方法3.2 處理中斷的典型方式3.3 注意事項4、線程中斷與線程終止的區別5、線程中斷的應用場景5.1 長時間運行任務的取消5.2 阻塞操作的快速響應5.3 服務或線程池的優雅關閉5.4 …

【LeetCode題解】LeetCode 33. 搜索旋轉排序數組

【題目鏈接】 33. 搜索旋轉排序數組 【題目描述】 【題解】 對于一個有序數組&#xff0c;我們可以使用二分查找算法來查找某個元素&#xff0c;具體的算法模板可以參考【算法基礎課-算法模板1】基礎算法中二分查找一節的內容。 然而&#xff0c;在這道題目中&#xff0c;數組…

使用 Serverless 架構快速構建基于 Iceberg 的事務型實時數據湖

文章目錄1. 背景介紹2. 架構設計3. 方案實現3.1 CDC3.1.1 自定義插件3.1.2 配置 MSK Connect3.2 實時攝入3.2.1 Glue 實現方案3.2.1.1 在 Glue 中創建 Kafka connection3.2.1.2 Glue Streaming 任務3.2.2 EMS Serverless 實現方案3.3 使用 Athena 查詢 Iceberg 表3.3.1 查詢3.3…

Java零基礎筆記20(Java高級技術:單元測試、反射、注解、動態代理)

1.單元測試2.反射2.1 反射第一步&#xff1a;加載類&#xff0c;獲取類的字節碼&#xff0c;class對象2.2 獲取類中的成分&#xff08;構造器、成員變量、成員方法&#xff09;&#xff0c;并對其進行操作獲取構造器的作用&#xff1a;獲取成員變量的作用&#xff1a;獲取成員…