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
- Open the credential in n8n and re-enter/refresh the API key or re-authorize OAuth, then retry.
- Confirm the credential has permission to call the provider's /models endpoint (some scopes restrict it).
- 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
- Validate credentials with a test call before populating the model dropdown.
- Surface this as a configuration error to the user, not a telemetry event (shouldReport is already false).
- For OAuth credentials, ensure token refresh works before listing models.
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
- OAuth access token expired and no refresh token is available
- No credentials found with specified filters
- You cannot use `--userId` and `--projectId` together. Use on
- You cannot use `--include` and `--exclude` together. Use one
- Provider connection type cannot be changed. Create a new con
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/30837a18fb160bac.
Report an issue: GitHub.