n8n-io/n8n · error · NotFoundError

404

404

Error message

Credential to be updated not found. You can only update credentials owned by you

What it means

Thrown inside the credential-update path when credentialsFinderService.findCredentialForUser(id,user,['credential:update'],{includeInstanceCredentials:true}) returns null — meaning no matching credential is visible to the user with the credential:update scope. HTTP 404. Like other credential errors, the message intentionally blends 'missing' and 'no-permission'. A logger.info records 'Attempt to update credential blocked due to lack of permissions' with the credentialId and userId.

Source

Thrown at packages/cli/src/credentials/credentials.controller.ts:255

		const {
			body,
			user,
			params: { credentialId },
		} = req;

		const credential = await this.credentialsFinderService.findCredentialForUser(
			credentialId,
			user,
			['credential:update'],
			{ includeInstanceCredentials: true },
		);

		if (!credential) {
			this.logger.info('Attempt to update credential blocked due to lack of permissions', {
				credentialId,
				userId: user.id,
			});
			throw new NotFoundError(
				'Credential to be updated not found. You can only update credentials owned by you',
			);
		}

		if (credential.isManaged) {
			throw new BadRequestError('Managed credentials cannot be updated');
		}

		const isChangingAuthType = body.type !== undefined && body.type !== credential.type;

		if (credential.usageScope === 'instance' && isChangingAuthType) {
			throw new BadRequestError(
				'Provider connection type cannot be changed. Create a new connection instead.',
			);
		}

		if (
			credential.usageScope === 'instance' &&

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Confirm via GET /credentials that the id is visible and that the user holds credential:update.
  2. Refresh access by being re-added to the owning project, or use a credential you own.
  3. Treat 404 here as terminal — do not retry without re-establishing access.
Defensive patterns

Strategy: validation

Validate before calling

async function canUpdateCredential(credentialId: string) {
  const r = await fetch(`/rest/credentials/${credentialId}`, { method: 'GET' });
  if (!r.ok) return false;
  const c = await r.json();
  return c.scopes?.includes('credential:update') ?? false;
}
if (!(await canUpdateCredential(id))) {
  throw new Error('No credential:update on this credential');
}

Type guard

const canUpdate = (c: { scopes?: string[] }) =>
  Array.isArray(c.scopes) && c.scopes.includes('credential:update');

Try / catch

try { await fetch(`/rest/credentials/${id}`, { method: 'PUT', body }); }
catch (e) { if (e.statusCode === 404) { /* refresh or regain access */ } else throw e; }

Prevention

When it happens

Trigger: PUT/PATCH /credentials/:id where :id does not exist OR exists but the user lacks credential:update on the owning project; also when includeInstanceCredentials:true still yields no row.

Common situations: User was removed from a project but still has the credential open in an editor tab; cross-tenant id; credential deleted; RBAC scope missing after migration.

Related errors


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