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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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;获取成员…