decolua/9router · error · Error

`Provider ${providerName} does not support device code flow`

Error message

`Provider ${providerName} does not support device code flow`

What it means

requestDeviceCode(providerName, codeChallenge, options) first resolves the provider via getProvider, then asserts provider.flowType === 'device_code' before delegating to provider.requestDeviceCode. If the provider exists but uses a different flow (e.g. authorization_code_pkce), it cannot initiate a device-code session, so this error is thrown instead of calling an undefined/incorrect method.

Source

Thrown at src/lib/oauth/providers/index.js:144

    : provider.config;

  const tokens = await provider.exchangeToken(config, code, redirectUri, codeVerifier, state, meta || {});

  let extra = null;
  if (provider.postExchange) {
    extra = await provider.postExchange(tokens);
  }

  return provider.mapTokens(tokens, extra);
}

/**
 * Request device code (for device_code flow)
 */
export async function requestDeviceCode(providerName, codeChallenge, options) {
  const provider = getProvider(providerName);
  if (provider.flowType !== "device_code") {
    throw new Error(`Provider ${providerName} does not support device code flow`);
  }
  return await provider.requestDeviceCode(provider.config, codeChallenge, options || {});
}

/**
 * Poll for token (for device_code flow)
 * @param {string} providerName - Provider name
 * @param {string} deviceCode - Device code from requestDeviceCode
 * @param {string} codeVerifier - PKCE code verifier (optional for some providers)
 * @param {object} extraData - Extra data from device code response (e.g. clientId/clientSecret for Kiro)
 */
export async function pollForToken(providerName, deviceCode, codeVerifier, extraData) {
  const provider = getProvider(providerName);
  if (provider.flowType !== "device_code") {
    throw new Error(`Provider ${providerName} does not support device code flow`);
  }

  const result = await provider.pollToken(provider.config, deviceCode, codeVerifier, extraData);

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Check provider.flowType (via getProvider(name).flowType) before choosing the flow; only call requestDeviceCode when it equals 'device_code'
  2. For flowType 'authorization_code_pkce' (or default), use generateAuthData(providerName, redirectUri, meta) to get authUrl/state/codeVerifier and complete the browser + callback/exchangeTokens flow instead
  3. If the provider should genuinely support device code, verify you are targeting the right provider name (e.g. kilocode, kiro) rather than a PKCE-only one
  4. Update calling code/UI to branch on flowType rather than hardcoding the device-code path

Example fix

// before
const dc = await requestDeviceCode('claude', codeChallenge); // throws: claude is not device_code
// after
import { getProvider, requestDeviceCode, generateAuthData } from '@/lib/oauth/providers/index.js';
const p = getProvider('claude');
const auth = p.flowType === 'device_code'
  ? await requestDeviceCode('claude', codeChallenge)
  : await generateAuthData('claude', redirectUri); // use auth.authUrl in browser flow
Defensive patterns

Strategy: type-guard

Validate before calling

import { getProvider } from '@/lib/oauth/providers/index.js';
function supportsDeviceCode(name) {
  try { return getProvider(name).flowType === 'device_code'; } catch { return false; }
}
// if (!supportsDeviceCode(name)) use generateAuthData() browser flow instead

Type guard

function isDeviceCodeProvider(name) {
  const p = getProvider(name);
  return p != null && p.flowType === 'device_code' && typeof p.requestDeviceCode === 'function';
}

Try / catch

try {
  const dc = await requestDeviceCode(name, challenge);
} catch (e) {
  if (e.message.includes('does not support device code flow')) {
    const auth = await generateAuthData(name, redirectUri);
    // redirect user to auth.authUrl instead
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling requestDeviceCode for a provider whose module sets flowType to 'authorization_code_pkce' or another non-device flow — e.g. requestDeviceCode('claude', challenge) or requestDeviceCode('github', challenge), where the correct entry point is generateAuthData + the browser auth URL flow instead.

Common situations: A generic OAuth client that always uses the device-code path regardless of provider; UI or CLI offering 'sign in with device code' for every provider; provider migrations that changed flowType; confusing providers with similar names (kilocode is device_code, but most others are not).

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/f0115e5006ed3c1a. Report an issue: GitHub.