n8n-io/n8n · error · CredentialNotFoundError

0

0

Error message

Credential with ID "${credentialId}" was not found.

What it means

Thrown by CredentialsService.testById() when no stored credential matches the given ID, OR when the credential exists but is not project-scoped (e.g. it is an instance/provider credential). The method is reserved for the dynamic-credential test flow; instance credentials must be tested through testWithCredentials. CredentialNotFoundError extends UserError, surfacing as a not-found (HTTP 404) response.

Source

Thrown at packages/cli/src/credentials/credentials.service.ts:1218

					`This credential is assigned to credential use "${result.credentialUseIds.join(', ')}" and cannot be deleted`,
				);
			}
			return;
		}

		await this.credentialsRepository.remove(credential);
	}

	async test(userId: User['id'], credentials: ICredentialsDecrypted) {
		return await this.credentialsTester.testCredentials(userId, credentials.type, credentials);
	}

	async testById(userId: User['id'], credentialId: string) {
		const storedCredential = await this.credentialsFinderService.findCredentialById(credentialId);

		// Dynamic-credential flows only; admins test instance credentials via testWithCredentials
		if (!storedCredential || storedCredential.usageScope !== 'project') {
			throw new CredentialNotFoundError(credentialId);
		}

		const credentials = await this.prepareCredentialsForTest({ storedCredential });
		return await this.test(userId, credentials);
	}

	async testWithCredentials(user: User, credentials: ICredentialsDecrypted) {
		const storedCredential = await this.credentialsFinderService.findCredentialForUser(
			credentials.id,
			user,
			['credential:read'],
			{ includeInstanceCredentials: true },
		);

		if (!storedCredential) {
			if (credentials.id === '' && hasGlobalScope(user, 'credential:manageInstance')) {
				this.validateInstanceCredentialData(credentials.data ?? {});
				return await this.test(user.id, credentials);

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Confirm the credential ID exists via GET /credentials/{id} before testing.
  2. For instance/provider credentials, use the testWithCredentials flow (testWithCredentials endpoint) instead of testById.
  3. Refresh the credentials list in the UI so the test button targets a current ID.
  4. If the credential was deleted, recreate it or pick a valid one from the current list.

Example fix

// before
await credentialsService.testById(userId, maybeInstanceId); // throws

// after
const stored = await credentialsFinderService.findCredentialById(id);
if (!stored || stored.usageScope !== 'project') {
  return credentialsService.testWithCredentials(user, { id, type, data });
}
await credentialsService.testById(userId, id);
Defensive patterns

Strategy: validation

Validate before calling

const stored = await credentialsFinderService.findCredentialById(credentialId);
if (!stored || stored.usageScope !== 'project') {
  // route to testWithCredentials instead
}
await credentialsService.testById(userId, credentialId);

Type guard

function isProjectCredentialId(id: unknown): id is string {
  return typeof id === 'string' && id.trim().length > 0;
}

Try / catch

try {
  await credentialsService.testById(userId, credentialId);
} catch (e) {
  if (e instanceof CredentialNotFoundError) {
    // refresh credential list, fall back to testWithCredentials for instance creds
  }
  throw e;
}

Prevention

When it happens

Trigger: POST /credentials/{id}/test where {id} does not exist; calling testById on an instance-scoped (provider connection) credential, which the dynamic flow does not support; testing a credential that was deleted between page load and click.

Common situations: Stale credential ID in a deep link or bookmark; frontend test button routed to the wrong endpoint for instance credentials; deleted credential still listed in a cached UI; off-by-one copy/paste of an ID.

Related errors


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