{"record":{"id":"43f255c6c1a7cc40","repo":"n8n-io/n8n","slug":"403-43f255","errorCode":"403","errorMessage":"Forbidden","messagePattern":"Forbidden","errorType":"exception","errorClass":"ForbiddenError","httpStatus":403,"severity":"error","filePath":"packages/cli/src/credentials/credentials.controller.ts","lineNumber":184,"sourceCode":"\t@ProjectScope('credential:read')\n\tasync probeCredentials(\n\t\treq: AuthenticatedRequest,\n\t\t_res: unknown,\n\t\t@Param('credentialId') credentialId: string,\n\t) {\n\t\ttry {\n\t\t\tconst result = await this.credentialsService.probeById(req.user, credentialId);\n\n\t\t\tthis.eventService.emit('credentials-probed', {\n\t\t\t\tuser: req.user,\n\t\t\t\tcredentialId,\n\t\t\t\toutcome: result.outcome,\n\t\t\t});\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tif (error instanceof CredentialNotFoundError) {\n\t\t\t\tthrow new ForbiddenError();\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t@Post('/')\n\tasync createCredentials(\n\t\treq: AuthenticatedRequest,\n\t\t_: Response,\n\t\t@Body payload: CreateCredentialDto,\n\t) {\n\t\tconst newCredential = await this.credentialsService.createUnmanagedCredential(\n\t\t\tpayload,\n\t\t\treq.user,\n\t\t);\n\n\t\tconst project = await this.sharedCredentialsRepository.findCredentialOwningProject(","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/cli/src/credentials/credentials.controller.ts#L166-L202","documentation":"Thrown by the credential probe endpoint (POST against the persisted test URL) when credentialsService.probeById() raises CredentialNotFoundError. Same masking pattern as /test: the controller re-throws ForbiddenError() — its default message is the string 'Forbidden' (HTTP 403) — to avoid disclosing whether the credential is missing or merely forbidden.","triggerScenarios":"Probing a credential whose id is unknown or not accessible to req.user; the service raises CredentialNotFoundError and the controller converts it to a generic 403 'Forbidden'.","commonSituations":"Generic credential types (e.g. Templated Custom Auth) being probed after the user lost access; id from another environment; credential deleted between selection and probe.","solutions":["Verify the credential is owned/shared with the current user via GET /credentials before probing.","Treat a 403 'Forbidden' from the probe endpoint as 'not accessible' and refresh the list.","Ensure the user has credential:read on the owning project."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"async function canAccessCredential(credentialId: string) {\n  const r = await fetch(`/rest/credentials/${credentialId}`);\n  return r.ok;\n}\nif (!(await canAccessCredential(id))) {\n  throw new Error('Credential not accessible; cannot probe');\n}","typeGuard":null,"tryCatchPattern":"try { await fetch(`/rest/credentials/${id}/probe`, { method: 'POST' }); }\ncatch (e) { if (e.statusCode === 403 && /forbidden/i.test(e.message)) { /* refresh list */ } else throw e; }","preventionTips":["Verify credential:read on the owning project before probing.","Treat a generic 403 'Forbidden' from probe as 'not accessible'.","Do not cache credential access across project moves."],"tags":["credentials","probe","rest-api","forbidden","information-hiding","authorization"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}