{"record":{"id":"4f13a785d5be4c7d","repo":"can1357/oh-my-pi","slug":"xiaomi-token-plan-token-plan-region-names-regio","errorCode":null,"errorMessage":"Xiaomi Token Plan (${TOKEN_PLAN_REGION_NAMES[region]})","messagePattern":"Xiaomi Token Plan \\((.+?)\\)","errorType":"exception","errorClass":"AIError.OnPromptRequiredError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/registry/oauth/xiaomi.ts","lineNumber":190,"sourceCode":"\tconst trimmed = apiKey.trim();\n\tif (!trimmed) {\n\t\tthrow new AIError.ApiKeyRequiredError();\n\t}\n\n\toptions.onProgress?.(`Validating ${PROVIDER_ID} API key...`);\n\tawait validateXiaomiApiKey(trimmed, undefined, options.signal, fetchImpl);\n\treturn trimmed;\n}\n\n/**\n * Login to a regional Xiaomi Token Plan endpoint.\n *\n * Prompts for a token-plan API key and validates it against the selected region.\n */\nexport async function loginXiaomiTokenPlan(options: OAuthController, region: XiaomiTokenPlanRegion): Promise<string> {\n\tconst fetchImpl = options.fetch ?? fetch;\n\tif (!options.onPrompt) {\n\t\tthrow new AIError.OnPromptRequiredError(`Xiaomi Token Plan (${TOKEN_PLAN_REGION_NAMES[region]})`);\n\t}\n\toptions.onAuth?.({\n\t\turl: TOKEN_PLAN_AUTH_URL,\n\t\tinstructions: `Copy your token-plan API key for the ${TOKEN_PLAN_REGION_NAMES[region]} region`,\n\t});\n\tconst apiKey = await options.onPrompt({\n\t\tmessage: `Paste your Xiaomi Token Plan ${TOKEN_PLAN_REGION_NAMES[region]} API key (tp-...)`,\n\t\tplaceholder: \"tp-...\",\n\t});\n\tif (options.signal?.aborted) {\n\t\tthrow new AIError.LoginCancelledError();\n\t}\n\tconst trimmed = apiKey.trim();\n\tif (!trimmed) {\n\t\tthrow new AIError.ApiKeyRequiredError();\n\t}\n\n\toptions.onProgress?.(`Validating Xiaomi Token Plan (${TOKEN_PLAN_REGION_NAMES[region]}) API key...`);","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/registry/oauth/xiaomi.ts#L172-L208","documentation":"loginXiaomiTokenPlan performs interactive OAuth-style login for Xiaomi's token plan. The library requires a prompt callback so the caller can collect the API key from the user; if the OAuthController has no onPrompt, it aborts with OnPromptRequiredError before doing any network work. This is a programming/configuration error in the caller, not a runtime condition.","triggerScenarios":"loginXiaomiTokenPlan (or one of the region providers xiaomiTokenPlanAms/Cn/SgpProvider) is invoked with an OAuthController that lacks an onPrompt function, e.g. headless/SDK usage that only sets fetch or onProgress.","commonSituations":"Embedding the login flow in a script or CI job with no TTY; constructing the controller without wiring the UI callback; running in a non-interactive environment where the app skips prompt registration.","solutions":["Provide an onPrompt callback on the OAuthController that surfaces a text input to the user and resolves with the pasted tp-... API key.","If no interaction is possible, bypass loginXiaomiTokenPlan and configure the API key directly via the provider's api-key entry path.","Detect non-interactive environments up front and catch OnPromptRequiredError to report that interactive login is unavailable."],"exampleFix":"// before\nawait loginXiaomiTokenPlan({ signal, fetch }, \"cn\");\n// after\nawait loginXiaomiTokenPlan({ signal, fetch, onPrompt: async opts => window.prompt(opts.message) ?? \"\" }, \"cn\");","handlingStrategy":"try-catch","validationCode":"if (typeof controller.onPrompt !== \"function\") throw new Error(\"Interactive Xiaomi login requires an onPrompt callback\");","typeGuard":"function canPrompt(c): c is typeof c & { onPrompt: (o: { message: string; placeholder?: string }) => Promise<string> } { return typeof c.onPrompt === \"function\"; }","tryCatchPattern":"try { await loginXiaomiTokenPlan(controller, region); }\ncatch (e) { if (e instanceof AIError.OnPromptRequiredError) { console.error(\"Interactive login unavailable; set the API key directly.\"); return; } throw e; }","preventionTips":["Always wire onPrompt before invoking any OAuth login function","Detect non-TTY/headless environments early and use direct API-key configuration instead","Keep the OAuthController construction centralized so callbacks are never omitted"],"tags":["oauth","interactive-login","missing-callback","xiaomi"],"backgroundTag":"missing-prompt-callback","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}