{"record":{"id":"6e711c0e040343e3","repo":"can1357/oh-my-pi","slug":"qwen-token-api-key-is-required","errorCode":null,"errorMessage":"Qwen token/API key is required","messagePattern":"Qwen token/API key is required","errorType":"validation","errorClass":"AIError.ApiKeyRequiredError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/registry/qwen-portal.ts","lineNumber":31,"sourceCode":"\t}\n\n\toptions.onAuth?.({\n\t\turl: AUTH_URL,\n\t\tinstructions: \"Copy your Qwen OAuth token or API key\",\n\t});\n\n\tconst token = await options.onPrompt({\n\t\tmessage: \"Paste your Qwen OAuth token or API key\",\n\t\tplaceholder: \"sk-...\",\n\t});\n\n\tif (options.signal?.aborted) {\n\t\tthrow new AIError.LoginCancelledError();\n\t}\n\n\tconst trimmed = token.trim();\n\tif (!trimmed) {\n\t\tthrow new AIError.ApiKeyRequiredError(\"Qwen token/API key is required\");\n\t}\n\n\toptions.onProgress?.(\"Validating credentials...\");\n\tawait validateOpenAICompatibleApiKey({\n\t\tprovider: \"qwen-portal\",\n\t\tapiKey: trimmed,\n\t\tbaseUrl: API_BASE_URL,\n\t\tmodel: VALIDATION_MODEL,\n\t\tsignal: options.signal,\n\t});\n\n\treturn trimmed;\n}\n\nexport const qwenPortalProvider = {\n\tid: \"qwen-portal\",\n\tname: \"Qwen Portal\",\n\tlogin: (cb: OAuthLoginCallbacks) => loginQwenPortal(cb),","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/registry/qwen-portal.ts#L13-L49","documentation":"loginQwenPortal requires a non-empty Qwen token or API key. When the user submits an empty (or whitespace-only) string at the prompt, it throws AIError.ApiKeyRequiredError with the message \"Qwen token/API key is required\". The library refuses to continue to credential validation with an empty secret.","triggerScenarios":"The onPrompt callback resolves with an empty string or whitespace-only string, so trimmed is empty and the guard throws.","commonSituations":"User just presses Enter at the paste prompt; clipboard paste silently failed so the input is blank; a scripted/automated caller wires onPrompt to something that returns \"\".","solutions":["Ensure the prompt UI collects an actual token (Qwen OAuth token or sk-... API key) and re-run loginQwenPortal.","Add pre-submit validation in the onPrompt UI to prevent submitting an empty value.","If driving programmatically, make the onPrompt implementation return the real credential or throw a distinct cancellation error instead of \"\".","Run `qwen` OAuth flow (device/portal) to obtain a valid token before attempting login."],"exampleFix":"// before\nawait loginQwenPortal({ onPrompt: async () => clipboard.readText() ?? \"\", onProgress });\n// after\nawait loginQwenPortal({\n  onPrompt: async () => {\n    const t = clipboard.readText()?.trim();\n    if (!t) throw new Error(\"clipboard-empty\");\n    return t;\n  },\n  onProgress,\n});","handlingStrategy":"validation","validationCode":"const token = await options.onPrompt({ message: \"Paste your Qwen OAuth token or API key\", placeholder: \"sk-...\" });\nif (!token || !token.trim()) {\n  // re-prompt or abort before calling loginQwenPortal internals\n  throw new Error(\"token-empty\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  await loginQwenPortal({ onPrompt, signal });\n} catch (err) {\n  if (err instanceof AIError.ApiKeyRequiredError) {\n    ui.notify(\"A Qwen token or API key is required.\");\n    return;\n  }\n  throw err;\n}","preventionTips":["Validate non-empty input in the prompt UI before resolving onPrompt.","Distinguish 'user cancelled' (throw a cancel error) from 'empty input' (re-prompt) in scripted onPrompt implementations.","Verify clipboard paste actually produced content before submitting."],"tags":["auth","api-key","validation","qwen"],"backgroundTag":"missing-api-key","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}