Cursor 接 OpenAI 兼容 API 后 401?按 settings.json、Base URL、模型名和认证方式定位
Cursor 接 OpenAI 兼容 API 后 401?按 settings.json、Base URL、模型名和认证方式定位
Cursor 接入 OpenAI 兼容 API 后,如果测试请求返回 401 Unauthorized,先不要换模型,也不要把错误扩大成“服务不兼容”。最短的排查顺序是:确认当前版本的设置入口,再确认 Base URL 是否只承担版本前缀,接着用 /models 读真实模型 ID,最后检查认证方式是否真的发出了 Bearer 头。
本文只解决一个问题:Cursor 配置自定义 OpenAI 兼容端点后出现 401,如何按配置、路径、模型和认证四层定位。文章中的命令运行在 127.0.0.1 本地夹具上,用合成值复现 401 与 200;没有请求线上模型,也没有使用 Cursor 账号或真实 API Key。
先找到当前版本的配置位置
适用环境是桌面版 Cursor,且当前版本的 Settings 页面提供 Models 或自定义模型入口。如果界面里有 Edit in settings.json,从这个按钮打开文件;如果没有,就留在当前版本的图形配置页,不要凭旧文章去猜 ~/.cursor 下的固定路径。Cursor 的菜单和 JSON 字段会随版本、平台和发行渠道变化,本文把 settings.json 当作“当前安装主动打开的配置文件”,不是一个对所有版本都成立的绝对路径。
进入配置后先做一份本地备份,只改一个模型条目。下面是为了说明排查字段而写的最小字段表,不是可直接覆盖所有 Cursor 版本的通用 JSON 模板:
| 要核对的值 | 示例 | 先判断什么 |
| 模型显示名 | Local Cursor Fixture | 只方便在界面里识别,不等于服务端模型 ID |
| Base URL | http://127.0.0.1:端口/v1 | 只放主机和 API 版本前缀,不放 /chat/completions |
| Model ID | fixture-chat-model | 以 /v1/models 返回的 data[].id 为准 |
| API Key / Auth | fixture-key-not-real | 仅作本地哨兵值,不能写入文章、截图或日志 |
如果当前版本的 JSON 编辑器需要一个结构化条目,可以先把它当成字段检查清单来读,不要盲目粘贴未知 schema:
{
"model": "fixture-chat-model",
"baseUrl": "http://127.0.0.1:端口/v1",
"apiKey": "fixture-key-not-real"
}
真正需要确认的是当前 Cursor UI 对应的字段,而不是这三个键的拼写。保存后重新打开设置页,检查值是否回显;如果保存后回到旧值,先解决配置没有持久化的问题,再看 401。
用一个本地命令先建立基线
在修改 Cursor 前,先运行这个脱敏夹具:
python3 06-evidence/probe_cursor_401.py
成功信号是下面五行同时出现:
BAD_AUTH_MODELS_STATUS=401 CODE=invalid_api_key
GOOD_MODELS_STATUS=200 MODEL_IDS=fixture-chat-model
BAD_AUTH_CHAT_STATUS=401 CODE=invalid_api_key
GOOD_CHAT_STATUS=200 CONTENT=fixture ok
WRONG_PATH_STATUS=404 PATH=/v1/v1/chat/completions
这里故意把“坏认证”“好认证”和“重复路径”放在一次运行里。这样你不会把 401、404 和模型名错误混成一个问题。
第一层:401 先看认证,不要先换模型
如果 /v1/models 已经返回 401,模型字段还没有进入服务端的有效业务判断。先检查当前工具是否发送了正确的认证头,以及 Key 是否属于当前 Base URL 对应的服务。对于 OpenAI 风格端点,最小请求通常会带:
Authorization: Bearer <your-key>
Content-Type: application/json
真实 Key 只应保存在本地密钥管理或应用提供的安全字段中。文章中的 fixture-key-not-real 只是回环服务的哨兵值,不可替换成任何线上凭据。排查日志时只保留 auth=present、状态码和最终路径,不要打印 Authorization 的内容。
如果工具的 /models 能 200,但对话仍 401,比较两次请求是否使用了同一个认证来源。有的客户端把模型列表请求和对话请求分成两条代码路径;这时不要只看界面里 Key 字段“已经填写”,要看脱敏后的请求日志是否都带认证。
第二层:Base URL 只放公共前缀
把 Base URL 和具体操作分开写:
Base URL: http://127.0.0.1:端口/v1
模型列表: /models
对话接口: /chat/completions
正确结果: /v1/models
/v1/chat/completions
下面是常见的重复拼接:
Base URL: http://127.0.0.1:端口/v1
接口路径: /v1/chat/completions
最终路径: /v1/v1/chat/completions
本地结果中的 WRONG_PATH_STATUS=404 只证明这个夹具拒绝了重复路径。真实网关可能使用不同的版本前缀,所以修复时要以服务端文档和最终请求日志为准。不要因为 404 去换模型,也不要用增加重试掩盖路径错误。
第三层:用 /models 返回值核对模型名
认证和地址通过后,再看模型 ID。先请求:
curl -sS "$BASE_URL/models" \
-H "Authorization: Bearer $API_KEY"
从返回 JSON 的 data[].id 原样复制模型名。fixture-chat-model 是本地夹具的真实返回值;Local Cursor Fixture 是展示名,两者不是同一个概念。大小写、连字符、版本后缀都可能参与服务端匹配,不能因为另一个客户端里有同名模型,就推断当前端点也接受它。
模型列表 200 只说明列表请求通过,不能说明对话能力、工具调用或流式能力都通过。用返回的 ID 再做一次最小对话请求:
curl -sS "$BASE_URL/chat/completions" \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"model":"fixture-chat-model","messages":[{"role":"user","content":"ping"}]}'
成功信号应是 HTTP 200,并且 choices[0].message.content 非空。只有列表和对话都通过,才可以回到 Cursor 里点发送;不能用一个 200 结果替代另一条请求的验证。
按现场决定下一步
| 现象 | 第一判断 | 修复动作 | 不要做什么 |
/models 返回 401 | 认证层未通过 | 核对 Key 来源、Bearer 方式和当前端点 | 不要换模型名 |
/models 200、对话 401 | 两条请求的认证来源可能不同 | 比较脱敏后的请求头和配置回显 | 不要只看 UI 的 Key 输入框 |
/models 404 | 主机或版本前缀不对 | 查最终 URL,修正 Base URL | 不要重试一个不存在的路径 |
列表 200、对话 model_not_found | 模型 ID 不一致 | 复制 data[].id 并重新保存 | 不要用展示名猜 ID |
| 本地命令 200、Cursor 仍 401 | Cursor 侧字段或认证映射不同 | 回到当前版本设置页,确认保存值和请求日志 | 不要声称服务端完全兼容 |
这次实测的边界
本文只做一次透明测试环境披露:命令运行在 Python 本地 HTTP 服务上,监听地址为 127.0.0.1,Key、模型名、返回文本均为合成值。我们没有运行 Cursor 桌面客户端,没有读取 Cursor 配置文件,没有访问线上供应商,也没有把任何真实凭据写入日志。Cursor 的实际设置入口和字段以你安装版本的可见界面与官方文档为准。
总结
Cursor 遇到 401 时,按“认证 -> 路径 -> 模型 -> 客户端映射”的顺序收敛。先用 /models 判断认证和 Base URL,再用服务端返回的模型 ID发起最小对话,最后回到当前 Cursor 版本的设置页确认保存和请求来源。这样可以把 401、404 和 model_not_found 分开处理,避免反复换模型或把一次本地成功误写成线上兼容承诺。
更多推荐


所有评论(0)