n8n-io/n8n · error · Error
No credential template for type "${credentialType}" — add on
Error message
No credential template for type "${credentialType}" — add one to evaluations/credentials/seeder.ts What it means
Thrown by `createOneCredential()` in credentials/seeder.ts when the requested `credentialType` has no entry in the `CREDENTIAL_TEMPLATES` record. The template record is the closed registry of credential types the eval harness knows how to seed (slackApi, notionApi, githubApi, gmailOAuth2, googleDriveOAuth2Api, linearMcpOAuth2Api, notionMcpOAuth2Api, microsoftTeamsOAuth2Api, whatsAppTriggerApi, googlePalmApi, httpHeaderAuth, httpBearerAuth, httpBasicAuth, openAiApi). The exported `SUPPORTED_CREDENTIAL_TYPES` Set is the authoritative list the case-file schema validates against, so a well-formed case should never reach this throw — it's a guard against drift between the schema and the seeder.
Source
Thrown at packages/@n8n/instance-ai/evaluations/credentials/seeder.ts:138
* Create a single credential of the given type. Throws on an unknown type and
* on creation failure — callers decide what a failure means for their flow
* (declared-credential seeding fails the build; a mid-run "create" decision
* falls back to decline, see `user-proxy/tools.ts`).
*
* `usedNames` de-dupes display names across calls that share it (e.g. every
* declared credential in one `createDeclaredCredentials` batch) by appending
* `#2`, `#3`, ... — pass a fresh `Map` for an unrelated, independent batch.
*/
export async function createOneCredential(
client: N8nClient,
credentialType: string,
name: string | undefined,
usedNames: Map<string, number>,
options?: { logger?: EvalLogger },
): Promise<CreatedCredential> {
const template = CREDENTIAL_TEMPLATES[credentialType];
if (!template) {
throw new Error(
`No credential template for type "${credentialType}" — add one to evaluations/credentials/seeder.ts`,
);
}
const base = name ?? template.defaultName;
const count = (usedNames.get(base) ?? 0) + 1;
usedNames.set(base, count);
const resolvedName = count > 1 ? `${base} #${count}` : base;
const envToken = template.envVar ? process.env[template.envVar] : undefined;
const token = envToken ?? PLACEHOLDER_TOKEN;
options?.logger?.verbose(` Creating credential ${resolvedName} (${credentialType})`);
// No retry: a credential POST isn't idempotent, so retrying after a lost response would orphan a duplicate we never capture for cleanup.
const { id } = await client.createCredential(
resolvedName,
credentialType,
template.buildData(token),
);View on GitHub (pinned to 5ac6606e81)
Solutions
- Open evaluations/credentials/seeder.ts and add a new entry to `CREDENTIAL_TEMPLATES` keyed by the exact type string from the error, with `defaultName`, optional `envVar`, and a `buildData(token)` that returns the credential's data shape.
- Confirm `SUPPORTED_CREDENTIAL_TYPES` (derived from the templates' keys) now includes the type — the case-file schema will pick it up automatically.
- If the type is a typo in the case JSON (e.g. `slackApiV2` vs `slackApi`), fix the JSON instead of adding a template.
Example fix
// before (seeder.ts)
const CREDENTIAL_TEMPLATES = {
slackApi: { ... },
// missing: airtableApi
};
// after
const CREDENTIAL_TEMPLATES = {
slackApi: { ... },
airtableApi: {
defaultName: '[eval] Airtable',
envVar: 'EVAL_AIRTABLE_API_KEY',
buildData: (key) => ({ apiKey: key }),
},
}; Defensive patterns
Strategy: type-guard
Validate before calling
import { SUPPORTED_CREDENTIAL_TYPES } from './seeder';
function isSupportedCredentialType(t: string): boolean {
return SUPPORTED_CREDENTIAL_TYPES.has(t);
}
// before seeding
for (const c of declared) {
if (!isSupportedCredentialType(c.type)) {
throw new Error(`Unsupported credential type: ${c.type}`);
}
} Type guard
function isKnownCredentialType(type: string, known: ReadonlySet<string>): type is string {
return known.has(type);
} Prevention
- Keep the case-file zod schema's allow-list and `CREDENTIAL_TEMPLATES` derived from a single source (`SUPPORTED_CREDENTIAL_TYPES`) so they can't drift.
- When adding a new integration to the suite, add the template and a case file in the same PR.
- Run a quick `SUPPORTED_CREDENTIAL_TYPES.has(type)` check in tests that exercise new case files.
When it happens
Trigger: A test case JSON declares a credential type the seeder hasn't been taught (e.g. a newly added n8n credential type used in a workflow before someone extends CREDENTIAL_TEMPLATES). The case-file schema check (`SUPPORTED_CREDENTIAL_TYPES`) was bypassed or drifted out of sync. A mid-run agent decision to create a credential (`UserProxyLlm`'s create-credential tool) named a type not in the registry.
Common situations: Adding a new integration to the eval suite: the workflow case file references a credential type, but no one added a template. Renaming a credential type in n8n core without updating both the schema allow-list and the templates map.
Related errors
- LangSmithTelemetry creates its own tracer — do not use .otlp
- No suspended run found for runId: ${this.runId}
- Invalid resume payload: ${parseResult.error}
- Episodic memory requires a resolved embedding model before r
- Unsupported provider: "${provider}". Supported providers: ${
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/a5abc8aea1c80faf.
Report an issue: GitHub.