diff --git a/packages/cli/src/i18n/locales/de.js b/packages/cli/src/i18n/locales/de.js
index 3893bfe04..b2444fa7b 100644
--- a/packages/cli/src/i18n/locales/de.js
+++ b/packages/cli/src/i18n/locales/de.js
@@ -1251,6 +1251,8 @@ export default {
'Das kostenlose Qwen OAuth-Kontingent wurde am 2026-04-15 eingestellt. Führen Sie /auth aus, um den Anbieter zu wechseln.',
'Qwen OAuth free tier was discontinued on 2026-04-15. Please select Coding Plan or API Key instead.':
'Das kostenlose Qwen OAuth-Kontingent wurde am 2026-04-15 eingestellt. Bitte wählen Sie Coding Plan oder API Key.',
+ 'Qwen OAuth free tier was discontinued on 2026-04-15. Please select a model from another provider or run /auth to switch.':
+ 'Das kostenlose Qwen OAuth-Angebot wurde am 2026-04-15 eingestellt. Bitte wählen Sie ein Modell eines anderen Anbieter oder führen Sie /auth aus, um zu wechseln.',
'\n⚠ Qwen OAuth free tier was discontinued on 2026-04-15. Please select another option.\n':
'\n⚠ Das kostenlose Qwen OAuth-Kontingent wurde am 2026-04-15 eingestellt. Bitte wählen Sie eine andere Option.\n',
'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models':
diff --git a/packages/cli/src/i18n/locales/en.js b/packages/cli/src/i18n/locales/en.js
index 4d3971859..1c99dcf7d 100644
--- a/packages/cli/src/i18n/locales/en.js
+++ b/packages/cli/src/i18n/locales/en.js
@@ -1304,6 +1304,8 @@ export default {
'Qwen OAuth free tier was discontinued on 2026-04-15. Run /auth to switch provider.',
'Qwen OAuth free tier was discontinued on 2026-04-15. Please select Coding Plan or API Key instead.':
'Qwen OAuth free tier was discontinued on 2026-04-15. Please select Coding Plan or API Key instead.',
+ 'Qwen OAuth free tier was discontinued on 2026-04-15. Please select a model from another provider or run /auth to switch.':
+ 'Qwen OAuth free tier was discontinued on 2026-04-15. Please select a model from another provider or run /auth to switch.',
'\n⚠ Qwen OAuth free tier was discontinued on 2026-04-15. Please select another option.\n':
'\n⚠ Qwen OAuth free tier was discontinued on 2026-04-15. Please select another option.\n',
'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models':
diff --git a/packages/cli/src/i18n/locales/fr.js b/packages/cli/src/i18n/locales/fr.js
index 6c0d01b9c..95cf5f935 100644
--- a/packages/cli/src/i18n/locales/fr.js
+++ b/packages/cli/src/i18n/locales/fr.js
@@ -1335,6 +1335,8 @@ export default {
'Le niveau gratuit Qwen OAuth a été abandonné le 2026-04-15. Exécutez /auth pour changer de fournisseur.',
'Qwen OAuth free tier was discontinued on 2026-04-15. Please select Coding Plan or API Key instead.':
'Le niveau gratuit Qwen OAuth a été abandonné le 2026-04-15. Veuillez sélectionner Coding Plan ou API Key.',
+ 'Qwen OAuth free tier was discontinued on 2026-04-15. Please select a model from another provider or run /auth to switch.':
+ "Le niveau gratuit de Qwen OAuth a été abandonné le 2026-04-15. Veuillez sélectionner un modèle d'un autre fournisseur ou exécuter /auth pour changer.",
'\n⚠ Qwen OAuth free tier was discontinued on 2026-04-15. Please select another option.\n':
'\n⚠ Le niveau gratuit Qwen OAuth a été abandonné le 2026-04-15. Veuillez sélectionner une autre option.\n',
'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models':
diff --git a/packages/cli/src/i18n/locales/ja.js b/packages/cli/src/i18n/locales/ja.js
index a6ba3c4cb..f0a1e9846 100644
--- a/packages/cli/src/i18n/locales/ja.js
+++ b/packages/cli/src/i18n/locales/ja.js
@@ -972,6 +972,8 @@ export default {
'Qwen OAuth 無料枠は 2026-04-15 に終了しました。/auth を実行してプロバイダーを切り替えてください。',
'Qwen OAuth free tier was discontinued on 2026-04-15. Please select Coding Plan or API Key instead.':
'Qwen OAuth 無料枠は 2026-04-15 に終了しました。Coding Plan または API Key を選択してください。',
+ 'Qwen OAuth free tier was discontinued on 2026-04-15. Please select a model from another provider or run /auth to switch.':
+ 'Qwen OAuth無料プランは2026-04-15に終了しました。他のプロバイダーのモデルを選択するか、/authを実行して切り替えてください。',
'\n⚠ Qwen OAuth free tier was discontinued on 2026-04-15. Please select another option.\n':
'\n⚠ Qwen OAuth 無料枠は 2026-04-15 に終了しました。他のオプションを選択してください。\n',
'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models':
diff --git a/packages/cli/src/i18n/locales/pt.js b/packages/cli/src/i18n/locales/pt.js
index 4bd728890..935db9f40 100644
--- a/packages/cli/src/i18n/locales/pt.js
+++ b/packages/cli/src/i18n/locales/pt.js
@@ -1257,6 +1257,8 @@ export default {
'O nível gratuito do Qwen OAuth foi descontinuado em 2026-04-15. Execute /auth para trocar de provedor.',
'Qwen OAuth free tier was discontinued on 2026-04-15. Please select Coding Plan or API Key instead.':
'O nível gratuito do Qwen OAuth foi descontinuado em 2026-04-15. Selecione Coding Plan ou API Key.',
+ 'Qwen OAuth free tier was discontinued on 2026-04-15. Please select a model from another provider or run /auth to switch.':
+ 'O nível gratuito do Qwen OAuth foi descontinuado em 2026-04-15. Por favor, selecione um modelo de outro provedor ou execute /auth para trocar.',
'\n⚠ Qwen OAuth free tier was discontinued on 2026-04-15. Please select another option.\n':
'\n⚠ O nível gratuito do Qwen OAuth foi descontinuado em 2026-04-15. Selecione outra opção.\n',
'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models':
diff --git a/packages/cli/src/i18n/locales/ru.js b/packages/cli/src/i18n/locales/ru.js
index 02a3d234a..5ee2bb6dc 100644
--- a/packages/cli/src/i18n/locales/ru.js
+++ b/packages/cli/src/i18n/locales/ru.js
@@ -1181,6 +1181,8 @@ export default {
'Бесплатный уровень Qwen OAuth прекращён 2026-04-15. Выполните /auth для смены провайдера.',
'Qwen OAuth free tier was discontinued on 2026-04-15. Please select Coding Plan or API Key instead.':
'Бесплатный уровень Qwen OAuth прекращён 2026-04-15. Выберите Coding Plan или API Key.',
+ 'Qwen OAuth free tier was discontinued on 2026-04-15. Please select a model from another provider or run /auth to switch.':
+ 'Бесплатный уровень Qwen OAuth был прекращен 2026-04-15. Пожалуйста, выберите модель от другого провайдера или выполните /auth для переключения.',
'\n⚠ Qwen OAuth free tier was discontinued on 2026-04-15. Please select another option.\n':
'\n⚠ Бесплатный уровень Qwen OAuth прекращён 2026-04-15. Выберите другую опцию.\n',
'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models':
diff --git a/packages/cli/src/i18n/locales/zh.js b/packages/cli/src/i18n/locales/zh.js
index 39eabbe1b..59c05bf1a 100644
--- a/packages/cli/src/i18n/locales/zh.js
+++ b/packages/cli/src/i18n/locales/zh.js
@@ -1233,6 +1233,8 @@ export default {
'Qwen OAuth 免费额度已于 2026-04-15 停用。请运行 /auth 切换服务商。',
'Qwen OAuth free tier was discontinued on 2026-04-15. Please select Coding Plan or API Key instead.':
'Qwen OAuth 免费额度已于 2026-04-15 停用。请选择 Coding Plan 或 API Key。',
+ 'Qwen OAuth free tier was discontinued on 2026-04-15. Please select a model from another provider or run /auth to switch.':
+ 'Qwen OAuth免费层已于2026-04-15停止服务。请选择其他提供商的模型或运行 /auth 切换。',
'\n⚠ Qwen OAuth free tier was discontinued on 2026-04-15. Please select another option.\n':
'\n⚠ Qwen OAuth 免费额度已于 2026-04-15 停用。请选择其他选项。\n',
'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models':
diff --git a/packages/cli/src/ui/components/ModelDialog.test.tsx b/packages/cli/src/ui/components/ModelDialog.test.tsx
index dc5cc108a..d9b563302 100644
--- a/packages/cli/src/ui/components/ModelDialog.test.tsx
+++ b/packages/cli/src/ui/components/ModelDialog.test.tsx
@@ -192,8 +192,8 @@ describe('', () => {
expect(mockedSelect).toHaveBeenCalledTimes(1);
});
- it('calls config.switchModel and onClose when DescriptiveRadioButtonSelect.onSelect is triggered', async () => {
- const { props, mockConfig, mockSettings } = renderComponent(
+ it('blocks qwen-oauth model selection with an error message (discontinued)', async () => {
+ const { props, mockConfig } = renderComponent(
{},
{
getAvailableModelsForAuthType: vi.fn((t: AuthType) => {
@@ -214,25 +214,79 @@ describe('', () => {
await childOnSelect(`${AuthType.QWEN_OAUTH}::${DEFAULT_QWEN_MODEL}`);
- expect(mockConfig?.switchModel).toHaveBeenCalledWith(
- AuthType.QWEN_OAUTH,
- DEFAULT_QWEN_MODEL,
+ // qwen-oauth is discontinued — switchModel should NOT be called
+ expect(mockConfig?.switchModel).not.toHaveBeenCalled();
+ // Dialog should NOT close (user stays in the dialog to see the error)
+ expect(props.onClose).not.toHaveBeenCalled();
+ });
+
+ it('calls config.switchModel and onClose when selecting a non-OAuth model', async () => {
+ const switchModel = vi.fn().mockResolvedValue(undefined);
+ const getAuthType = vi.fn(() => AuthType.USE_OPENAI);
+ const getAvailableModelsForAuthType = vi.fn((t: AuthType) => {
+ if (t === AuthType.USE_OPENAI) {
+ return [{ id: 'gpt-4', label: 'GPT-4', authType: t }];
+ }
+ if (t === AuthType.QWEN_OAUTH) {
+ return getFilteredQwenModels().map((m) => ({
+ id: m.id,
+ label: m.label,
+ authType: AuthType.QWEN_OAUTH,
+ }));
+ }
+ return [];
+ });
+
+ const { props, mockSettings } = renderComponent({}, {
+ getModel: vi.fn(() => 'gpt-4'),
+ getAuthType,
+ switchModel,
+ getAvailableModelsForAuthType,
+ getAllConfiguredModels: vi.fn(() => [
+ ...getFilteredQwenModels().map((m) => ({
+ id: m.id,
+ label: m.label,
+ description: m.description || '',
+ authType: AuthType.QWEN_OAUTH,
+ })),
+ {
+ id: 'gpt-4',
+ label: 'GPT-4',
+ description: 'GPT-4 model',
+ authType: AuthType.USE_OPENAI,
+ },
+ ]),
+ getContentGeneratorConfig: vi.fn(() => ({
+ authType: AuthType.USE_OPENAI,
+ model: 'gpt-4',
+ })),
+ } as unknown as Partial);
+
+ const childOnSelect = mockedSelect.mock.calls[0][0].onSelect;
+ expect(childOnSelect).toBeDefined();
+
+ // Select a non-OAuth model (USE_OPENAI)
+ await childOnSelect(`${AuthType.USE_OPENAI}::gpt-4`);
+
+ expect(switchModel).toHaveBeenCalledWith(
+ AuthType.USE_OPENAI,
+ 'gpt-4',
undefined,
);
expect(mockSettings.setValue).toHaveBeenCalledWith(
SettingScope.User,
'model.name',
- DEFAULT_QWEN_MODEL,
+ 'gpt-4',
);
expect(mockSettings.setValue).toHaveBeenCalledWith(
SettingScope.User,
'security.auth.selectedType',
- AuthType.QWEN_OAUTH,
+ AuthType.USE_OPENAI,
);
expect(props.onClose).toHaveBeenCalledTimes(1);
});
- it('calls config.switchModel and persists authType+model when selecting a different authType', async () => {
+ it('blocks switching to qwen-oauth from another authType (discontinued)', async () => {
const switchModel = vi.fn().mockResolvedValue(undefined);
const getAuthType = vi.fn(() => AuthType.USE_OPENAI);
const getAvailableModelsForAuthType = vi.fn((t: AuthType) => {
@@ -253,39 +307,25 @@ describe('', () => {
getAuthType,
getModel: vi.fn(() => 'gpt-4'),
getContentGeneratorConfig: vi.fn(() => ({
- authType: AuthType.QWEN_OAUTH,
- model: DEFAULT_QWEN_MODEL,
+ authType: AuthType.USE_OPENAI,
+ model: 'gpt-4',
})),
- // Add switchModel to the mock object (not the type)
switchModel,
getAvailableModelsForAuthType,
};
- const { props, mockSettings } = renderComponent(
+ const { props } = renderComponent(
{},
- // Cast to Config to bypass type checking, matching the runtime behavior
mockConfigWithSwitchAuthType as unknown as Partial,
);
const childOnSelect = mockedSelect.mock.calls[0][0].onSelect;
await childOnSelect(`${AuthType.QWEN_OAUTH}::${DEFAULT_QWEN_MODEL}`);
- expect(switchModel).toHaveBeenCalledWith(
- AuthType.QWEN_OAUTH,
- DEFAULT_QWEN_MODEL,
- { requireCachedCredentials: true },
- );
- expect(mockSettings.setValue).toHaveBeenCalledWith(
- SettingScope.User,
- 'model.name',
- DEFAULT_QWEN_MODEL,
- );
- expect(mockSettings.setValue).toHaveBeenCalledWith(
- SettingScope.User,
- 'security.auth.selectedType',
- AuthType.QWEN_OAUTH,
- );
- expect(props.onClose).toHaveBeenCalledTimes(1);
+ // qwen-oauth is discontinued — switchModel should NOT be called
+ expect(switchModel).not.toHaveBeenCalled();
+ // Dialog should NOT close
+ expect(props.onClose).not.toHaveBeenCalled();
});
it('passes onHighlight to DescriptiveRadioButtonSelect', () => {
diff --git a/packages/cli/src/ui/components/ModelDialog.tsx b/packages/cli/src/ui/components/ModelDialog.tsx
index e8ca53b59..a6488d5aa 100644
--- a/packages/cli/src/ui/components/ModelDialog.tsx
+++ b/packages/cli/src/ui/components/ModelDialog.tsx
@@ -213,11 +213,19 @@ export function ModelDialog({
const value =
isRuntime && snapshotId ? snapshotId : `${t2}::${model.id}`;
+ const isQwenOAuth = t2 === AuthType.QWEN_OAUTH;
+
const title = (
[{t2}]
@@ -225,16 +233,22 @@ export function ModelDialog({
{isRuntime && (
(Runtime)
)}
+ {isQwenOAuth && !isRuntime && (
+ ({t('Discontinued')})
+ )}
);
- // Include runtime indicator in description
+ // Include runtime / discontinued indicator in description
let description = model.description || '';
if (isRuntime) {
description = description
? `${description} (Runtime)`
: 'Runtime model';
}
+ if (isQwenOAuth && !isRuntime) {
+ description = t('Discontinued — switch to Coding Plan or API Key');
+ }
return {
value,
@@ -323,6 +337,25 @@ export function ModelDialog({
return;
}
+ // Block selection of discontinued qwen-oauth models
+ // (only block non-runtime OAuth; runtime OAuth models from existing
+ // cached tokens are still allowed to work until the server rejects them)
+ const isQwenOAuthSelection =
+ selected.startsWith(`${AuthType.QWEN_OAUTH}::`) ||
+ (selected.startsWith('$runtime|') &&
+ selected.split('|')[1] === AuthType.QWEN_OAUTH);
+ const isRuntimeOAuthSelection = selected.startsWith(
+ `$runtime|${AuthType.QWEN_OAUTH}|`,
+ );
+ if (isQwenOAuthSelection && !isRuntimeOAuthSelection) {
+ setErrorMessage(
+ t(
+ 'Qwen OAuth free tier was discontinued on 2026-04-15. Please select a model from another provider or run /auth to switch.',
+ ),
+ );
+ return;
+ }
+
let after: ContentGeneratorConfig | undefined;
let effectiveAuthType: AuthType | undefined;
let effectiveModelId = selected;
@@ -461,6 +494,14 @@ export function ModelDialog({
borderRight={false}
borderColor={theme.border.default}
/>
+ {highlightedEntry.authType === AuthType.QWEN_OAUTH &&
+ !highlightedEntry.isRuntime && (
+
+
+ ⚠ {t('Discontinued — switch to Coding Plan or API Key')}
+
+
+ )}