{"record":{"id":"a5abc8aea1c80faf","repo":"n8n-io/n8n","slug":"no-credential-template-for-type-credentialtype","errorCode":null,"errorMessage":"No credential template for type \"${credentialType}\" — add one to evaluations/credentials/seeder.ts","messagePattern":"No credential template for type \"(.+?)\" — add one to evaluations/credentials/seeder\\.ts","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/instance-ai/evaluations/credentials/seeder.ts","lineNumber":138,"sourceCode":" * Create a single credential of the given type. Throws on an unknown type and\n * on creation failure — callers decide what a failure means for their flow\n * (declared-credential seeding fails the build; a mid-run \"create\" decision\n * falls back to decline, see `user-proxy/tools.ts`).\n *\n * `usedNames` de-dupes display names across calls that share it (e.g. every\n * declared credential in one `createDeclaredCredentials` batch) by appending\n * `#2`, `#3`, ... — pass a fresh `Map` for an unrelated, independent batch.\n */\nexport async function createOneCredential(\n\tclient: N8nClient,\n\tcredentialType: string,\n\tname: string | undefined,\n\tusedNames: Map<string, number>,\n\toptions?: { logger?: EvalLogger },\n): Promise<CreatedCredential> {\n\tconst template = CREDENTIAL_TEMPLATES[credentialType];\n\tif (!template) {\n\t\tthrow new Error(\n\t\t\t`No credential template for type \"${credentialType}\" — add one to evaluations/credentials/seeder.ts`,\n\t\t);\n\t}\n\n\tconst base = name ?? template.defaultName;\n\tconst count = (usedNames.get(base) ?? 0) + 1;\n\tusedNames.set(base, count);\n\tconst resolvedName = count > 1 ? `${base} #${count}` : base;\n\n\tconst envToken = template.envVar ? process.env[template.envVar] : undefined;\n\tconst token = envToken ?? PLACEHOLDER_TOKEN;\n\toptions?.logger?.verbose(`  Creating credential ${resolvedName} (${credentialType})`);\n\t// No retry: a credential POST isn't idempotent, so retrying after a lost response would orphan a duplicate we never capture for cleanup.\n\tconst { id } = await client.createCredential(\n\t\tresolvedName,\n\t\tcredentialType,\n\t\ttemplate.buildData(token),\n\t);","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/instance-ai/evaluations/credentials/seeder.ts#L120-L156","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (seeder.ts)\nconst CREDENTIAL_TEMPLATES = {\n  slackApi: { ... },\n  // missing: airtableApi\n};\n// after\nconst CREDENTIAL_TEMPLATES = {\n  slackApi: { ... },\n  airtableApi: {\n    defaultName: '[eval] Airtable',\n    envVar: 'EVAL_AIRTABLE_API_KEY',\n    buildData: (key) => ({ apiKey: key }),\n  },\n};","handlingStrategy":"type-guard","validationCode":"import { SUPPORTED_CREDENTIAL_TYPES } from './seeder';\nfunction isSupportedCredentialType(t: string): boolean {\n  return SUPPORTED_CREDENTIAL_TYPES.has(t);\n}\n// before seeding\nfor (const c of declared) {\n  if (!isSupportedCredentialType(c.type)) {\n    throw new Error(`Unsupported credential type: ${c.type}`);\n  }\n}","typeGuard":"function isKnownCredentialType(type: string, known: ReadonlySet<string>): type is string {\n  return known.has(type);\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["credentials","seeder","eval-config","registry","developer-error"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}