{"record":{"id":"98a4a40016e52a00","repo":"can1357/oh-my-pi","slug":"deepseek-api-key-is-empty-after-stripping-bearer-p","errorCode":null,"errorMessage":"DeepSeek API key is empty after stripping Bearer prefix","messagePattern":"DeepSeek API key is empty after stripping Bearer prefix","errorType":"exception","errorClass":"AIError.ApiKeyRequiredError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/registry/deepseek.ts","lineNumber":26,"sourceCode":"\tauthUrl: \"https://platform.deepseek.com/api_keys\",\n\tinstructions: \"Create or copy your API key from the DeepSeek dashboard\",\n\tpromptMessage: \"Paste your DeepSeek API key\",\n\tplaceholder: \"sk-...\",\n\tvalidation: {\n\t\tkind: \"models-endpoint\",\n\t\tprovider: \"deepseek\",\n\t\tmodelsUrl: \"https://api.deepseek.com/v1/models\",\n\t},\n});\n\nexport function normalizeDeepSeekApiKey(raw: string): string {\n\tconst trimmed = raw.trim();\n\tif (!trimmed) {\n\t\treturn trimmed;\n\t}\n\tconst stripped = trimmed.replace(/^bearer\\b\\s*/i, \"\");\n\tif (!stripped) {\n\t\tthrow new AIError.ApiKeyRequiredError(\"DeepSeek API key is empty after stripping Bearer prefix\");\n\t}\n\treturn stripped;\n}\n\nexport const loginDeepSeek = async (options: OAuthController): Promise<string> => {\n\tconst userOnPrompt = options.onPrompt;\n\tconst wrapped: OAuthController = userOnPrompt\n\t\t? {\n\t\t\t\t...options,\n\t\t\t\tonPrompt: async (prompt: OAuthPrompt) => normalizeDeepSeekApiKey(await userOnPrompt(prompt)),\n\t\t\t}\n\t\t: options;\n\treturn innerLogin(wrapped);\n};\n\nexport const deepseekProvider = {\n\tid: \"deepseek\",\n\tname: \"DeepSeek\",","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/registry/deepseek.ts#L8-L44","documentation":"During DeepSeek API-key login, normalizeDeepSeekApiKey() trims the raw key and strips an optional case-insensitive 'Bearer' prefix. If the entire input consisted only of the Bearer prefix (and whitespace), nothing usable remains, so the library throws this ApiKeyRequiredError rather than storing an empty credential. It protects against storing a credential that would fail on every request with an auth error.","triggerScenarios":"Invoking loginDeepSeek (or any path calling normalizeDeepSeekApiKey) with a raw key string that, after trimming and removing the leading 'Bearer' token, is empty — e.g. the input is exactly \"Bearer\", \"bearer \", or only whitespace fails the earlier empty check and reaches the strip check only when a prefix is present.","commonSituations":"Pasting just the header line 'Bearer' from a curl example instead of the key; clipboard capture grabbing a truncated paste; scripts piping a token file that contains only the prefix; IDE/auto-complete inserting 'Bearer ' before an empty field.","solutions":["Re-enter the actual DeepSeek API key (from platform.deepseek.com API keys page) without the Bearer prefix.","If a prefix is included, ensure the key text follows it, e.g. 'Bearer sk-xxxx'.","Check clipboard/paste — re-copy the full key and confirm it starts with 'sk-'.","Verify the input source (file/stdin/env) actually contains the key, not just the header token."],"exampleFix":"// before\nawait loginDeepSeek({ onPrompt: async () => \"Bearer\" });\n// throws ApiKeyRequiredError\n// after\nawait loginDeepSeek({ onPrompt: async () => \"Bearer sk-abc123...\" });\n// or simply:\nawait loginDeepSeek({ onPrompt: async () => \"sk-abc123...\" });","handlingStrategy":"validation","validationCode":"// sanitize before passing the key into login\nconst raw = userInput.trim();\nconst key = /^bearer\\b\\s*/i.test(raw) ? raw.replace(/^bearer\\b\\s*/i, \"\") : raw;\nif (!key) throw new Error(\"Paste the DeepSeek key itself, not just the 'Bearer' prefix\");","typeGuard":"function isUsableApiKey(v: unknown): v is string {\n  return typeof v === \"string\" && v.trim().replace(/^bearer\\b\\s*/i, \"\").length > 0;\n}","tryCatchPattern":"try {\n  await loginDeepSeek({ onPrompt: promptForKey });\n} catch (err) {\n  if (err instanceof AIError.ApiKeyRequiredError) {\n    console.error(\"Input contained only a Bearer prefix — paste the full sk-... key\");\n  } else throw err;\n}","preventionTips":["Paste only the raw key (starts with sk-) without the 'Bearer ' header prefix.","Verify clipboard contents after copying from docs/curl examples.","Trim whitespace from scripted/env-sourced keys.","Validate key non-empty and non-prefix-only before storing."],"tags":["authentication","api-key","deepseek","input-validation"],"backgroundTag":"empty-api-key","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}