BigPizzaV3/CodexPlusPlus · error
请在设置中填写有效 API Key
Error message
请在设置中填写有效 API Key
What it means
For the external channel, apiConfig() requires a non-empty API key that contains no CR/LF characters. Header injection via newlines is blocked, and an empty key would produce an unusable Authorization header. This error means the key field was blank or contained line breaks.
Solutions
- Paste the provider API key into the API Key field in settings (keys are never persisted in tree checkpoints unless 'remember' is enabled)
- Strip whitespace/newlines from the pasted key before saving
- Re-enter the key if the app was restarted with remember=false, since it is intentionally not stored
- Get a new key from the provider if the old one is missing/revoked
Example fix
// before const key = pastedKey; // 'sk-abc\n' // after const key = pastedKey.replace(/[\r\n]/g, '').trim();
Defensive patterns
Strategy: validation
Validate before calling
const key = String(input?.key || '').trim();
if (!key || /[\r\n]/.test(key)) throw new Error('请在设置中填写有效 API Key'); Type guard
function hasValidKey(c) { return typeof c?.key === 'string' && c.key.trim().length > 0 && !/[\r\n]/.test(c.key); } Try / catch
let cfg;
try { cfg = apiConfig(formValues); } catch (e) { if (String(e).includes('API Key')) openSettingsAndFocusKeyField(); return; } Prevention
- Sanitize pasted keys with .replace(/[\r\n]/g,'').trim()
- Prompt for the key immediately when the user switches to the external channel
- Don't rely on remembered keys after restart when remember=false — check and re-prompt
- Use single-line password inputs that strip newlines on paste
When it happens
Trigger: apiConfig({channel:'external',...,key:''}) or key:'sk-abc\n malicious-header: x'; thrown through readApiForm/config when the settings form has no key or a multiline paste.
Common situations: User forgets to paste the API key when switching to external channel; key copied with a trailing newline from a terminal or PDF; key field contains multiple lines from a bad paste; remember=false and the stored config has no key after reload.
Understand the failure class
Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.
Related errors
- API Key 不能为空
- Chat Completions 上游 Key 不能为空
- 上游 Key 不能为空
- 图片上游 Key 不能为空
- 官方混合 API 不应在 auth.json 中保存 OPENAI_API_KEY。请清理此供应商的…
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/68b3b9c8be3e8d3a.
Report an issue: GitHub.
Appendix: source
Thrown at tools/conversation-canvas/external-api.mjs:16
// OpenAI-compatible Chat Completions. Never persist credentials in tree checkpoints.
export function apiEndpoint(raw){
let url;try{url=new URL(String(raw).trim());}catch{throw Error('请输入完整的 HTTPS API 地址');}
if(url.protocol!=='https:'||url.username||url.password||url.search||url.hash)throw Error('API 地址须使用 HTTPS,且不含账号、密码、查询参数或片段');
const path=url.pathname.replace(/\/+$/,'');
url.pathname=path.endsWith('/chat/completions')?path:(path||'/v1')+'/chat/completions';
return url.href;
}
export function apiConfig(input){
const channel=input?.channel==='external'?'external':'native';
const value={channel,baseUrl:String(input?.baseUrl||'').trim(),model:String(input?.model||'').trim(),key:String(input?.key||'').trim(),remember:input?.remember===true,speed:input?.speed==='provider'?'provider':'fast',revision:input?.revision||crypto.randomUUID()};
if(channel==='external'){
value.endpoint=apiEndpoint(value.baseUrl);
if(!value.model||value.model.length>200)throw Error('请填写 API 的模型名称');
if(!value.key||/[\r\n]/.test(value.key))throw Error('请在设置中填写有效 API Key');
}
return value;
}
export function storedApiConfig(config){
const {channel,baseUrl,model,remember,speed,revision}=config;
return {channel,baseUrl,model,remember,speed,revision,...remember?{key:config.key}:{}};
}
export function apiError(error){
if(error?.name==='AbortError')return error;
if(error?.canvasApiLocal===true)return error;
const code=Number(error?.status??error?.responseStatus);
const hint={401:'密钥无效或已过期',403:'接口拒绝访问,请检查权限',404:'地址或模型不存在',408:'接口请求超时',413:'本批资料超过接口大小限制',429:'接口限流或额度不足'}[code];
// Provider messages can echo request contents and Authorization; never display them.
return Object.assign(Error(hint?`API ${code}:${hint}`:code>=400?`API 请求失败(HTTP ${code}),请检查服务状态`:'API 连接失败,请检查地址、网络及服务状态'),{retryable:!code||code===408||code===429||code>=500});
}
View on GitHub (pinned to b1ed92e5e4)