认证、请求头与错误
所有 Gateway 端点用 Bearer:
Authorization: Bearer gw_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx真实的 Gateway Key 是 gw_ 加 43 个字符(32 字节 base64url)。缺失或格式不对返回 401 invalid_api_key。
Anthropic 兼容端点也接受 x-api-key。两个头同时出现且值不一致时直接判失败,返回 401 —— SDK 配了 base URL 又留着环境变量里的旧 key,就是这个场景。
Gateway 为每个请求生成一个 req_ 加 26 个字符的 ID(16 字节随机数,小写 base32),例如
req_k9x8v5fcwde34gqbn142t2r9qw。它在处理器入口第一步就写入响应,先于鉴权和限流,
所以 401、429 在内的错误响应同样带着它;流式请求的 ID 在首个内容字节之前就已发出。
两个协议门面放的位置不同,各自对齐官方惯例:
| 端点 | 位置 |
|---|---|
OpenAI 兼容(/v1/chat/completions 等) | 响应头 X-Request-ID,不在响应体里 |
Anthropic 兼容(/v1/messages) | 响应头 request-id;错误响应体还带顶层 request_id 字段 |
响应体里的 id(chatcmpl-...)是补全对象的 ID,和请求 ID 是两回事;OpenAI 格式的错误信封
(error.message/type/param/code)里没有请求 ID,只能从响应头拿。
SDK 已经替你解析好了这个头:
# OpenAI Python SDKresponse = client.chat.completions.create(...)print(response._request_id)
# 出错时异常对象上也有except openai.APIStatusError as e: print(e.request_id)// OpenAI Node SDKconst response = await client.chat.completions.create({ ... });console.log(response._request_id);Anthropic 官方 SDK 同样通过 _request_id(Python / Node)暴露 request-id 头。
需要完整响应头时,用 SDK 的 with_raw_response(Python)/ withResponse(Node)变体。
用它来报障。 把错误路径的请求 ID 记进应用自己的日志;向 Halro 管理员反馈问题时给出这个 ID,
就能直接定位网关侧对应的记录 —— 比贴 message 文本可靠得多,message 是脱敏后的展示文案,
不保证稳定。
没有 GET /v1/models
Section titled “没有 GET /v1/models”Halro 的 Gateway 不实现 /v1/models。很多 SDK 初始化后第一个动作是 models.list(),会拿到 404,响应体里带一句专门写给这种情况的说明:应用通过 Project 上配置的公开别名来指定模型。别名由管理员在 Route 上定义,Gateway 不向应用泄露它背后连的是哪个上游模型。
七个会返回 429 的地方
Section titled “七个会返回 429 的地方”同一个状态码,七个触发点,处理方式并不相同。注意 rate_limit_exceeded 这一个 code 横跨两处 —— 一处在鉴权之前:
| 触发点 | code | 何时发生 |
|---|---|---|
| 每源地址限流 | rate_limit_exceeded | 鉴权之前。匿名调用方能索取的工作量本身是有界的 |
| Project 每分钟请求数 | rate_limit_exceeded | 鉴权之后,管理员在 Project 上配置 |
| Project 每分钟 token 数 | token_rate_limit_exceeded | 同上 |
| Project 并发 | concurrency_limit_exceeded | 同上 |
| Deployment 并发 | deployment_concurrency_limit_exceeded | 管理员在 Deployment 上配置 |
| 全部可选 Provider 都到并发上限 | provider_concurrency_limit_exceeded | 同上 |
| 上游服务商限流 | provider_rate_limit | 上游,Halro 透传 |
Token Guard 不在这张表里。它拒绝时返回的是 403 token_guard_blocked(本次尝试的价格超出 Token Guard 的成本上限),不是 429。按 429 写重试逻辑会让它变成一个永远不会通过的死循环。
Retry-After 不是每次都有
Section titled “Retry-After 不是每次都有”Retry-After 只在能算出一个正数等待时长时才写入。每源限流与 Project 限流会带上它;Deployment 并发那条不设置;上游透传的 429 只在上游自己带了可解析的头时才有。客户端必须处理这个头缺失的情况,不能无条件读取。