{"record":{"id":"39a76306e8e08327","repo":"can1357/oh-my-pi","slug":"qwencloud-token-plan","errorCode":null,"errorMessage":"QwenCloud Token Plan","messagePattern":"QwenCloud Token Plan","errorType":"exception","errorClass":"AIError.OnPromptRequiredError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/registry/alibaba-token-plan.ts","lineNumber":27,"sourceCode":"import type { ProviderDefinition } from \"./types\";\n\nconst INTERNATIONAL_AUTH_URL = \"https://home.qwencloud.com/billing/subscription/token-plan-individual\";\nconst CHINA_AUTH_URL = \"https://www.aliyun.com/benefit/scene/tokenplan\";\n\n/**\n * Log in to the QwenCloud Token Plan provider.\n *\n * The Token Plan ships as two regionally separate products with\n * non-interchangeable keys — International (Singapore) and China (Beijing) —\n * so login first selects the region (or a custom base URL) before pasting the\n * key, mirroring {@link loginAlibabaCodingPlan}. The chosen region is validated\n * against its own `/models` endpoint and, when it diverges from the default\n * international endpoint, stored in the credential so inference and discovery\n * both target it (#6682).\n */\nexport async function loginAlibabaTokenPlan(options: OAuthController): Promise<string> {\n\tif (!options.onPrompt) {\n\t\tthrow new AIError.OnPromptRequiredError(\"QwenCloud Token Plan\");\n\t}\n\n\tconst endpointChoice = await options.onPrompt({\n\t\tmessage:\n\t\t\t\"Select QwenCloud Token Plan region: 1=International (default), 2=China (Beijing), 3=Custom — enter 1, 2, or 3\",\n\t\tplaceholder: \"1\",\n\t});\n\tif (options.signal?.aborted) {\n\t\tthrow new AIError.LoginCancelledError();\n\t}\n\n\tconst choice = endpointChoice.trim();\n\tlet baseUrl: string;\n\tlet authUrl: string;\n\tlet instructions: string;\n\tif (choice === \"2\") {\n\t\tbaseUrl = ALIBABA_TOKEN_PLAN_CN_BASE_URL;\n\t\tauthUrl = CHINA_AUTH_URL;","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/registry/alibaba-token-plan.ts#L9-L45","documentation":"loginAlibabaTokenPlan (QwenCloud Token Plan) is an interactive login that needs an onPrompt callback to ask which region endpoint to use (International/China Beijing/Custom). Without an onPrompt function on the OAuthController it throws OnPromptRequiredError('QwenCloud Token Plan') so the embedding application knows it must provide user interaction.","triggerScenarios":"Calling loginAlibabaTokenPlan with an options object lacking `onPrompt` — headless scripts, SDK embedding without prompt wiring, or a controller constructed without prompt hooks.","commonSituations":"CI/CD credential bootstrap; non-TTY environments; forgetting that token-plan login, unlike plain API-key setup, is region-interactive.","solutions":["Provide an OAuthController with an `onPrompt` implementation","Run login via the interactive CLI/agent that supplies prompt UI","Set up the Token Plan credential manually (API key in config) for unattended environments"],"exampleFix":"// before\nawait loginAlibabaTokenPlan(controllerWithoutPrompts);\n// after\nawait loginAlibabaTokenPlan({ ...controller, onPrompt: async q => promptUser(q.message) });","handlingStrategy":"type-guard","validationCode":"if (typeof controller?.onPrompt !== 'function') throw new Error('QwenCloud Token Plan login requires an interactive onPrompt callback');","typeGuard":"function canPrompt(c: OAuthController): c is OAuthController & { onPrompt: NonNullable<OAuthController['onPrompt']> } { return typeof c.onPrompt === 'function'; }","tryCatchPattern":"try { key = await loginAlibabaTokenPlan(controller); } catch (e) { if (e instanceof AIError.OnPromptRequiredError) key = await provisionKeyFromConfig(); else throw e; }","preventionTips":["Construct controllers via a shared factory that always wires onPrompt","Use config-file keys instead of interactive login in CI","Note that token-plan login is region-interactive and not headless-safe"],"tags":["oauth","interactive-login","configuration"],"backgroundTag":"onprompt-callback-required","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}