Windows 环境下 Python 网络请求 [Errno 22] Invalid argument 排查实录
1. 背景
- 项目:GalgameCharacterSkills —— Flask + LiteLLM / httpx,后台多线程调用 DeepSeek API。
- 环境:Windows 11,Python 3.14.2,开启了 TUN 虚拟网卡代理。
- 初始配置:流式传输开启,推理强度
high,模型deepseek-v4-pro。
2. 起因
网页端发起归纳任务后,所有切片全部报错:
[21:19:46] 切片 1 处理异常: [Errno 22] Invalid argument
[21:19:46] 切片 2 处理异常: [Errno 22] Invalid argument
...
[Errno 22] Invalid argument 是 Windows 底层 Winsock 错误码 WSAEINVAL (10022) 在 Python 中的映射,意思是尝试对一个已经损坏的 Socket 做操作。
3. 经过
3.1 改配置 → 无效
关掉流式传输、把推理强度改为默认,重新点“生成”,依然报同样错误。
3.2 改代码加调试 → 全部无效
为了抓到详细报错堆栈,先后尝试了三轮:
| 轮次 | 手段 | 现象 |
|---|---|---|
| 1 | traceback.print_exc() 打印到终端 |
终端无输出 |
| 2 | 写入 error.log 文件 |
文件未生成 |
| 3 | 注入到 Flask 网页活动日志 | 网页仍显示旧格式 |
每一轮的修改都没有生效。
3.3 发现原因
在终端里多次重启过 py main.py,但 Windows 下 Ctrl+C 只关闭了终端显示,没有杀掉 Flask 后台的多线程子进程。旧进程一直霸占着 5000 端口。新进程虽然也启动了,但浏览器请求被 Windows 优先路由给了旧进程——我的新代码根本没人执行。
3.4 解决
taskkill /f /im python.exe # 一刀切,杀掉所有 Python 进程
py main.py # 全新启动
再发起任务,所有 6 个切片顺利完成。
4. 原理:为什么改什么都无效?
4.1 连接池复用了损坏的 Socket
httpx 默认会缓存 TCP 连接到一个连接池里,下次请求时优先复用,而不是重新握手。
第一次报错后,那条 TCP 连接在系统底层已经损坏了。但因为旧进程没退出,这个坏 Socket 一直留在连接池里。之后每次点“生成”,httpx 都从池子里拿出同一条坏连接来用——操作一个坏 Socket,Windows 直接报 Errno 22。
新进程连接池是空的,TCP 全新握手,没有问题。
4.2 端口被旧进程劫持
为什么改代码不生效?因为根本没跑到新代码上:
- 旧进程占着 5000 端口
- 新进程虽然也绑定了同一个端口(Windows 允许),但浏览器请求优先发给旧进程
- 我改了三四次代码,响应永远是旧进程返回的
所以不是代码写错了,是旧进程还没死。
5. 结果
一句话:杀掉所有 Python 进程,重新启动,就好了。
至于初始的配置(流式传输、推理强度)是不是第一次报错的原因——这不重要。重要的是:第一次报错留下了坏连接,旧进程不退出就一直复用,改再多配置也绕不过去。
6. 总结
这次踩坑教会我一件事
Windows 下 Ctrl+C 杀不死带多线程的 Python 进程。改了代码发现没生效,先跑
taskkill /f /im python.exe再说。
排查速查表
| 现象 | 优先排除 |
|---|---|
| 改代码后网页行为完全不变 | 旧进程是否还在占端口 |
持续 [Errno 22] 网络错误 |
连接池是否缓存了坏连接 |
| 日志文件、终端输出全无 | 请求是否路由到了旧进程 |