{"record":{"id":"0bdb559f852b87db","repo":"n8n-io/n8n","slug":"400-0bdb55","errorCode":"400","errorMessage":"Managed credentials cannot be updated","messagePattern":"Managed credentials cannot be updated","errorType":"exception","errorClass":"BadRequestError","httpStatus":400,"severity":"warning","filePath":"packages/cli/src/credentials/credentials.controller.ts","lineNumber":261,"sourceCode":"\t\tconst credential = await this.credentialsFinderService.findCredentialForUser(\n\t\t\tcredentialId,\n\t\t\tuser,\n\t\t\t['credential:update'],\n\t\t\t{ includeInstanceCredentials: true },\n\t\t);\n\n\t\tif (!credential) {\n\t\t\tthis.logger.info('Attempt to update credential blocked due to lack of permissions', {\n\t\t\t\tcredentialId,\n\t\t\t\tuserId: user.id,\n\t\t\t});\n\t\t\tthrow new NotFoundError(\n\t\t\t\t'Credential to be updated not found. You can only update credentials owned by you',\n\t\t\t);\n\t\t}\n\n\t\tif (credential.isManaged) {\n\t\t\tthrow new BadRequestError('Managed credentials cannot be updated');\n\t\t}\n\n\t\tconst isChangingAuthType = body.type !== undefined && body.type !== credential.type;\n\n\t\tif (credential.usageScope === 'instance' && isChangingAuthType) {\n\t\t\tthrow new BadRequestError(\n\t\t\t\t'Provider connection type cannot be changed. Create a new connection instead.',\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\tcredential.usageScope === 'instance' &&\n\t\t\t(body.isGlobal === true || body.isResolvable === true)\n\t\t) {\n\t\t\tthrow new BadRequestError(\n\t\t\t\t'Provider connections cannot be globally shared or converted to end-user credentials',\n\t\t\t);\n\t\t}","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/cli/src/credentials/credentials.controller.ts#L243-L279","documentation":"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.","triggerScenarios":"PUT/PATCH /credentials/:id on a credential flagged isManaged (typically deployed via N8N_CREDENTIALS_* or instance-config tooling); any field update triggers the guard.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"function assertEditable(c: { isManaged?: boolean }) {\n  if (c.isManaged) throw new Error('Managed credential; edit at source config');\n}\n// fetch credential, then assertEditable(credential) before PUT/PATCH","typeGuard":"const isEditableCredential = (c: { isManaged?: boolean }) => c.isManaged !== true;","tryCatchPattern":"try { await fetch(`/rest/credentials/${id}`, { method: 'PUT', body }); }\ncatch (e) { if (e.statusCode === 400 && /managed/i.test(e.message)) { /* edit at source */ } else throw e; }","preventionTips":["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."],"tags":["credentials","managed","update","rest-api","bad-request"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}