n8n-io/n8n · warning · BadRequestError
400
400
Error message
Managed credentials cannot be updated
What it means
Thrown by the credential-update path when the resolved credential has isManaged === true. Managed credentials are provisioned externally (e.g. via config / deployment tooling) and are immutable from the API/UI to prevent drift. HTTP 400. Fires after the existence check, before auth-type/scope checks.
Source
Thrown at packages/cli/src/credentials/credentials.controller.ts:261
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' &&
(body.isGlobal === true || body.isResolvable === true)
) {
throw new BadRequestError(
'Provider connections cannot be globally shared or converted to end-user credentials',
);
}View on GitHub (pinned to 5ac6606e81)
Solutions
- Update the managed credential at its source (config/deployment manifest), not via the API.
- If user-editable is required, create a new non-managed credential and migrate references.
- Surface isManaged in the UI to disable the edit affordance.
Defensive patterns
Strategy: type-guard
Validate before calling
function assertEditable(c: { isManaged?: boolean }) {
if (c.isManaged) throw new Error('Managed credential; edit at source config');
}
// fetch credential, then assertEditable(credential) before PUT/PATCH Type guard
const isEditableCredential = (c: { isManaged?: boolean }) => c.isManaged !== true; Try / catch
try { await fetch(`/rest/credentials/${id}`, { method: 'PUT', body }); }
catch (e) { if (e.statusCode === 400 && /managed/i.test(e.message)) { /* edit at source */ } else throw e; } Prevention
- Check isManaged before offering the edit affordance in the UI.
- Update managed credentials via config/deployment, not the API.
- Create a new non-managed credential if user-editable is required.
When it happens
Trigger: PUT/PATCH /credentials/:id on a credential flagged isManaged (typically deployed via N8N_CREDENTIALS_* or instance-config tooling); any field update triggers the guard.
Common situations: Operator edits a managed credential in the UI thinking it is user-owned; automation that loops over all credentials and PATCHes them; environment where managed credentials were introduced after the editor UI cached the row.
Related errors
- 404
- Invalid nps survey state structure
- Cannot delete your own user
- Request to delete a user failed because the user to delete a
- 403
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/0bdb559f852b87db.
Report an issue: GitHub.