2026 年 10 月 28 日晚上,我给一个视频生成任务接入 ofox.io 目录中使用的第三方模型 ID wan-3.0-prime。密钥重新复制了两遍,服务端仍然只返回:
text
HTTP/1.1 401 Unauthorized
先说明模型身份:本文中的 wan-3.0-prime 仅指 当前平台使用的第三方模型 ID,不把它表述为阿里云官方名称或其他平台通用的模型 ID。
截至 2026 年 10 月 30 日,可核查的信息如下:
- 阿里云百炼官方模型列表:模型大全功能规格与计费-大模型服务平台百炼-阿里云_大模型服务平台百炼(Model Studio)-大模型服务平台百炼(Model Studio)-阿里云帮助中心
- 本文核查到的官方上游同系列名称为
wan-3.0 - 暂无足够的阿里云官方证据证明
wan-3.0-prime是阿里云官方模型名,也不能据此推断该裸名称能在其他 API 平台使用
该平台的模型目录和接口配置可能变化。调用时应以该平台控制台实际显示的完整 ID、Base URL 和鉴权文档为准。本文不对其他平台是否提供该模型作推断。
直接说排查结论:遇到 401,按照"提供商与端点 → 鉴权头 → 签名原文 → 区域 → 系统时钟 → 密钥权限"逐项检查,先不要随意更改模型名。最快的办法通常是绕过 SDK,发送一个符合当前提供商文档的最小 HTTP 请求。
根据 RFC 9110 第 15.5.2 节,401 表示请求缺少访问目标资源所需的有效身份验证凭据。生成 401 响应的服务器还应发送一个或多个 WWW-Authenticate 响应头,用来说明适用的认证方式。
这意味着 401 通常发生在模型真正执行之前。模型尚未开始处理任务时,修改 prompt 一般没有帮助。
为什么 wan-3.0-prime 会返回 401
只看 wan-3.0-prime 这个第三方 ID,无法确定代码实际调用了哪个 API 提供商、网关或区域端点。
不同平台的配置可能完全不同,包括:
- Base URL 和区域端点;
Authorization: Bearer、X-API-Key或api-key;- HMAC 签名字段及规范化顺序;
- 项目、组织和模型授权;
- 请求时间容差;
- 平台实际采用的完整模型 ID。
不要把一个平台的 Key、Base URL 或模型 ID 直接拿到另一个平台使用。
可以先按照下面的表格判断问题范围:
| 现象 | 优先检查 | 可能原因 |
|---|---|---|
| 所有受保护接口都返回 401 | Key、域名、鉴权头 | 当前服务没有接受凭据 |
| 只有某个区域返回 401 | 区域端点、签名中的 region | Key、临时凭据或签名可能绑定区域 |
| curl 成功,SDK 失败 | SDK 默认域名、代理、旧环境变量 | 封装层可能改写了请求 |
| 网页端能用,API 返回 401 | API 权限、项目权限 | 网页会话与 API Key 可能相互独立 |
| 偶发 401,重新获取凭据后成功 | 系统时钟、临时凭据 | 时间戳偏差或凭据临近失效 |
| 只有特定模型失败 | 模型 ID、模型权限、项目授权 | Key 可能有效,但没有对应资源权限 |
整个排查链路可以压缩成:
方案一:核对 Key、Base URL 和鉴权头
首先确认 API Key 到底属于哪个平台。
实际项目中很常见的一类错误是:Key 在 A 平台创建,代码却把请求发到了 B 平台的域名。Key 本身未必损坏,但接收请求的服务无法识别它。
1. 检查环境变量是否真的生效
不要打印完整密钥,只输出 URL、密钥长度和少量前缀:
python
import os
url = os.getenv("API_URL")
key = os.getenv("API_KEY")
assert url, "API_URL 未设置"
assert key, "API_KEY 未设置"
print("API_URL:", url)
print("API_KEY:", len(key), key[:4] + "***")
重点检查:
API_URL是否属于创建这把 Key 的平台;- Key 长度是否与控制台复制出的内容一致;
- 前缀是否对应刚创建或计划使用的 Key;
- 运行进程是否重新加载了环境变量。
终端中更新 .env,不代表已经启动的常驻进程会自动读取新值。更新配置后,应根据运行环境重启进程、容器或任务实例。
还要检查 Key 前后是否有空格、换行或多余引号:
python
import os
raw = os.environ["API_KEY"]
print("prefix:", repr(raw[:6]))
print("length:", len(raw))
assert raw == raw.strip(), "API_KEY 前后存在空白字符"
这里只打印截断后的前缀,以便发现 \n、空格或引号。不要把完整密钥写入日志、监控平台或公开 Issue。
2. 确认鉴权头格式
Bearer Token 的常见写法是:
http
Authorization: Bearer YOUR_API_KEY
其他平台可能要求:
http
X-API-Key: YOUR_API_KEY
或者:
http
api-key: YOUR_API_KEY
这些格式不能凭经验混用,也不要同时把三种 Header 全部发送。必须按照实际 API 提供商当前有效的接口文档配置。
使用 Bearer 方式时,Bearer 与凭据之间需要一个空格:
text
Bearer␠YOUR_API_KEY
下面这种写法是错误的:
text
BearerYOUR_API_KEY
如果经过反向代理、API 网关或自建中转服务,还要确认中间层没有删除或覆盖 Authorization。
3. 记录 WWW-Authenticate 和请求标识
不要只记录响应正文。认证提示通常位于响应 Header 中,例如:
text
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
调试时至少记录:
- HTTP 状态码;
WWW-Authenticate;Date;- 平台返回的请求 ID 或追踪 ID;
- 响应正文中的错误码;
- 实际请求的 Host 和 Path。
不要记录完整 Key、Cookie、临时 Token 或签名。
Python 中可以这样查看:
python
print("status:", response.status_code)
print("www-authenticate:", response.headers.get("WWW-Authenticate"))
print("date:", response.headers.get("Date"))
print("request-id:", response.headers.get("X-Request-ID"))
print("body:", response.text)
请求 ID 的 Header 名称由平台决定,也可能使用其他名称,应以提供商文档和实际响应为准。
方案二:绕过 SDK,发送可复现的最小请求
这一步的目的,是把 SDK、模型参数和业务逻辑暂时排除,只验证"端点 + 鉴权方式 + Key"是否能被服务端接受。
API_URL 必须填写提供商文档明确支持的鉴权测试端点、模型列表端点或其他受保护的 GET 端点。不要把视频生成的 POST 地址直接改成 GET,否则可能得到 404 或 405,反而增加干扰。
python
import os
import requests
url = os.environ["API_URL"]
key = os.environ["API_KEY"]
response = requests.get(
url,
headers={
"Authorization": f"Bearer {key}",
"Accept": "application/json",
},
timeout=30,
allow_redirects=False,
)
print("status:", response.status_code)
print("www-authenticate:", response.headers.get("WWW-Authenticate"))
print("location:", response.headers.get("Location"))
print("body:", response.text)
这里暂时关闭自动重定向,是为了避免请求跳转到另一个域名后,鉴权头被客户端移除或发送到错误端点。
curl 也可以运行一次:
bash
curl -i --max-time 30 "$API_URL" \
-H "Authorization: Bearer $API_KEY" \
-H "Accept: application/json"
如果使用了 --fail 或 --fail-with-body,curl 还可能输出类似信息:
text
curl: (22) The requested URL returned error: 401
结果可以这样理解:
| 最小请求结果 | 可以得出的结论 | 下一步 |
|---|---|---|
| 200 | 该端点接受了当前基础鉴权 | 对比 SDK 的实际请求 |
| 401 | 凭据、域名、鉴权格式或凭据状态仍有问题 | 核对平台并轮换 Key |
| 403 | 服务可能已经识别身份,但拒绝访问 | 检查项目、资源和策略权限 |
| 404 | 请求路径、版本或区域可能错误 | 对照端点文档 |
| 405 | 当前 HTTP Method 不受支持 | 改用文档指定的方法 |
| 3xx | 请求发生重定向 | 检查 Location 和最终域名 |
这些解释不是所有平台都必须遵守的固定映射。最终仍要结合响应正文、响应头和平台文档判断。
如果 curl 成功而 SDK 失败,优先检查:
- SDK 实际使用的 Base URL;
- SDK 是否读取了旧环境变量;
- 代理是否改写了 Header;
- SDK 是否自动跳转到其他域名;
- SDK 是否采用了不同的 API 版本;
- SDK 是否错误地转换了模型 ID;
- 同名环境变量是否被系统级配置覆盖。
方案三:核对签名字符串、区域和系统时钟
有些接口不使用简单的 Bearer Token,而是采用 HMAC、临时凭据或云端签名鉴权。
这时 Key 正确,只能说明签名原材料可能正确,不能证明最终签名有效。
签名字符串必须逐字节一致
签名原文可能包含以下字段:
text
HTTP_METHOD
REQUEST_PATH
CANONICAL_QUERY
BODY_SHA256
TIMESTAMP
这只是常见结构,不是 wan-3.0-prime 的通用签名规范。实际平台还可能加入:
Host;region;- 签名算法和版本;
- 已签名 Header 列表;
- 会话 Token;
- 内容类型;
- 请求日期;
- 服务名称。
少一个换行、路径多一个 /、Query 编码方式不同,或者 JSON 被重新序列化,都可能生成不同的签名。
调试时建议记录:
- HTTP Method;
- 编码后的 Path;
- 规范化 Query;
- 请求体 SHA-256;
- UTC 时间戳;
- Host 和 region;
- 已签名 Header 名称;
- 最终签名的少量前缀;
- 服务端返回的请求 ID。
不要记录 Secret、完整签名或临时 Token。
请求体哈希可以单独计算:
python
import hashlib
with open("body.json", "rb") as file:
body = file.read()
print(hashlib.sha256(body).hexdigest())
这个哈希必须对应最终发送到网络上的字节。若 SDK 在发送前重新序列化 JSON,参与签名的内容与实际请求体就可能不同。
区域端点和签名区域必须一致
如果请求发往区域 A,签名字符串却填写区域 B,即使 Key 有效,也可能认证失败。
逐项比较:
text
实际请求 Host
签名中的 Host
实际请求 Path
签名中的 canonical URI
实际请求区域
签名中的 region
不要只替换 Base URL。签名中的 Host、region、服务名称和 canonical URI 也可能需要同步修改。
最稳妥的方法,是把脱敏后的最终 HTTP 请求展开,与当前提供商的官方签名示例逐字段比较。
检查系统时钟
签名请求通常带时间戳。系统时间偏差过大时,服务端可能认为请求尚未生效或已经过期。
查看本机 UTC:
bash
date -u
查看服务器响应中的 Date:
bash
curl -sI "$API_URL" | grep -i '^date:'
时间偏差可以表示为:
text
clock_skew = abs(local_utc - server_date)
允许偏差没有统一数值,必须查实际提供商的签名文档,不能直接假定为 5 分钟。
容器、虚拟机、休眠后的开发机以及未正常同步时间的服务器,都可能出现时钟漂移。还应检查 NTP 或系统时间同步服务是否正常工作。
方案四:检查项目、组织和模型权限
网页端可以使用某项功能,不等于 API Key 一定拥有相同权限。
网页订阅、浏览器 Cookie、登录会话和 API Key 可能属于不同的授权体系。控制台中至少要确认:
- API 功能是否已开通;
- Key 是否归属正确项目;
- 项目是否启用计费;
- 账户是否存在欠费或风控限制;
- Key 是否已撤销;
- Key 是否设置了有效期;
- Key 是否具备视频生成权限;
- 当前区域是否开放目标资源;
- 组织策略是否限制该 Key;
- 第三方目录中的模型 ID 是否仍然有效。
模型名错误时,常见结果可能是 400 或 404,但状态码由平台自行实现。有些网关也可能为了隐藏资源存在性而返回 401 或 403。
因此,看到 401 时仍应先检查认证链路,然后再检查模型 ID 和模型权限。
关于 wan-3.0-prime 的名称边界
本文只按以下边界使用这个名称:
| 名称 | 本文中的含义 |
|---|---|
wan-3.0-prime |
ofox.io 使用的第三方模型 ID |
wan-3.0 |
核查到的官方上游同系列名称 |
截至 2026 年 10 月 30 日,本文没有取得足够的阿里云官方证据,证明 wan-3.0-prime 是阿里云官方模型名称。因此:
- 不应把
wan-3.0-prime写成通用模型名; - 不应假设其他平台接受这个 ID;
- 不应把第三方目录中的命名直接当成上游官方规范;
- 切换平台时,应重新查询目标平台实际列出的完整模型 ID;
- 如果当前平台后续调整目录,应以其控制台和当前文档为准。
本文也不再声称该平台后台一定支持按 Model、User 或 API Key 查询结果与 Token 消耗,因为没有提供可按核查日期复现的后台文档或公开存档。若控制台确实提供请求日志,可将其作为辅助证据,但不能替代客户端和服务端日志。
切换渠道不是简单更换域名。Key、Base URL、模型 ID、请求参数、签名方式和错误码都需要按照目标平台文档重新配置。
常见问题 FAQ
Q1:wan-3.0-prime API 为什么返回 401 Unauthorized?
这里的 wan-3.0-prime 是当前平台的第三方模型 ID。401 表示服务端没有接受当前身份凭据。
优先检查:
- Key 是否属于当前域名对应的平台;
- 鉴权头格式是否正确;
- Key 是否已撤销或过期;
- 请求是否被代理或重定向;
- 项目、组织或资源权限是否满足要求。
同时查看 WWW-Authenticate、响应正文和请求 ID。
Q2:API Key 已填写,为什么还是 401?
常见原因包括:
- 环境变量没有生效;
- 常驻进程仍在使用旧 Key;
- Key 前后带空格或换行;
- Key 已撤销或过期;
- 请求发到了错误域名;
- Bearer 格式缺少空格;
- 平台实际要求的不是 Bearer;
- 代理删除了
Authorization; - 临时凭据缺少会话 Token;
- 签名时间、区域或 Host 不匹配。
可以打印 Key 长度和少量前缀进行排查,但不要输出完整密钥。
Q3:wan-3.0-prime 写错会导致 401 吗?
模型 ID 错误更常见的结果是 400 或 404,但这不是强制规则。部分平台可能返回 401、403 或自定义错误码。
因此应结合以下信息判断:
WWW-Authenticate;- 响应正文中的平台错误码;
- 请求 ID;
- 当前平台模型目录;
- 项目与模型权限;
- 当前平台的错误码文档。
不要只根据一个 HTTP 状态码猜测原因。
Q4:为什么网页端能用,API 调用却失败?
网页会话和 API Key 可能属于两套授权体系。
浏览器中可以生成视频,只能证明网页账户拥有相应功能,不能证明当前 API Key 具备相同权限。还需要单独确认 API 开通状态、项目计费、区域开放情况和模型权限。
Q5:curl 成功,但 Python SDK 仍然返回 401 怎么办?
把 SDK 最终发送的以下内容脱敏后打印出来,与 curl 逐项比较:
- URL;
- HTTP Method;
- Host;
- Path 和 Query;
- Header 名称;
- Body Hash;
- 时间戳;
- region;
- 是否发生重定向。
重点检查代理环境变量、SDK 默认 Base URL、旧配置、API 版本,以及 SDK 是否重新序列化了参与签名的 JSON 请求体。
Q6:是否应该直接把 wan-3.0-prime 改成 wan-3.0?
不应该盲目替换。
wan-3.0-prime 是本文讨论的 ofox.io 第三方 ID,wan-3.0 是核查到的官方上游同系列名称。两者不能仅凭名称相似就视为可互换。
应先查询当前请求平台实际列出的模型 ID。如果平台要求 wan-3.0-prime,就使用该平台给出的 ID;如果改用官方上游接口,则按照官方模型列表和接口文档重新配置端点、鉴权和参数。
我的最终排查顺序
现在我的排查顺序固定为七步:
- 确认
wan-3.0-prime只是当前平台使用的第三方 ID; - 用最小请求验证 Key、域名和基础鉴权;
- 记录
WWW-Authenticate、响应正文和请求 ID; - 核对 Bearer、
X-API-Key或签名认证方式; - 对比 Host、Path、region 和 Body Hash;
- 检查 UTC 时间、临时凭据和项目权限;
- 最后核对当前平台实际列出的完整模型 ID 和 SDK 映射。
这套方法不花哨,但每一步都有明确结果。遇到 401 时先盯住认证链路,不要一开始就修改 prompt、反复重装依赖或连续创建多把 Key。只有确认凭据和端点已经匹配之后,模型 ID 和业务参数的排查才有意义。