n8n-io/n8n · error · UserError

Models couldn't be loaded. Check that the selected credentia

Error message

Models couldn't be loaded. Check that the selected credential is valid and has the required permissions, then try again.

What it means

When listing a provider's models, the shared request helper treats HTTP 401/403 specially: the credential used for the request is missing, invalid, or lacks permission to call the models endpoint, so it throws a UserError (shouldReport:false) with an actionable message instead of a raw status. This routes the failure to the user as a configuration problem, not a telemetry-worthy bug.

Source

Thrown at packages/@n8n/ai-utilities/src/model-discovery/request.ts:19

import { UserError } from 'n8n-workflow';

import type { ListModelsFn, ListModelsOptions, ProviderModel } from './types';

/** GET a provider endpoint and parse JSON, treating rejected credentials as user errors. */
export async function getJson(
	url: string,
	headers: Record<string, string>,
	options: ListModelsOptions,
	provider: string,
): Promise<unknown> {
	const fetchFn = options.fetch ?? globalThis.fetch;
	const response = await fetchFn(url, {
		method: 'GET',
		headers: { ...headers, ...options.headers },
	});
	if (!response.ok) {
		if (response.status === 401 || response.status === 403) {
			throw new UserError(
				"Models couldn't be loaded. Check that the selected credential is valid and has the required permissions, then try again.",
				{ shouldReport: false },
			);
		}

		const body = await response.text().catch(() => '');
		throw new Error(
			`Failed to list ${provider} models (status ${response.status})${body ? `: ${body.slice(0, 500)}` : ''}`,
		);
	}
	return await response.json();
}

/** Resolve the API base: caller override or the provider default, without a trailing slash. */
export function baseUrl(options: ListModelsOptions, fallback: string): string {
	return (options.baseURL ?? fallback).replace(/\/+$/, '');
}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Open the credential in n8n and re-enter/refresh the API key or re-authorize OAuth, then retry.
  2. Confirm the credential has permission to call the provider's /models endpoint (some scopes restrict it).
  3. Verify the credential type matches the provider (e.g. don't bind an OpenAI key to the Anthropic node).
Defensive patterns

Strategy: try-catch

Type guard

import { UserError } from '@n8n/utils/error';
function isModelAuthError(e: unknown): boolean {
  return e instanceof UserError && /Models couldn't be loaded/.test(e.message);
}

Try / catch

try {
  await listModelsForProvider(provider, opts);
} catch (e) {
  if (e instanceof UserError && /Models couldn't be loaded/.test(e.message)) {
    // prompt user to fix the credential, then retry
  } else throw e;
}

Prevention

When it happens

Trigger: The credential selected for the chat model / agents feature has an expired/revoked API key; the key is correct but lacks the models:list scope; the wrong credential type is bound to the node; OAuth token refresh failed silently.

Common situations: API key revoked or rotated but not updated in n8n credentials; an Org/enterprise key without model-listing permission; OAuth credential whose token expired; copy-paste error introduced whitespace into the key.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/30837a18fb160bac. Report an issue: GitHub.