← 返回文章列表

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] 网络错误 连接池是否缓存了坏连接
日志文件、终端输出全无 请求是否路由到了旧进程
💬 评论区