n8n published tag [email protected] on 14 September 2026. The patch adds two payloadless log streaming events, n8n.instanceReporting.success and n8n.instanceReporting.failed, so a self hosted instance can see whether an instance usage report reached the central monitoring service. The same tag also fixes revoke and reconnect for end user credentials when the session user has no credential:update scope.
The full release notes and downloads are on the GitHub release page. The notes list one core feature and one editor bug fix, compared with [email protected] on the compare view. This is a patch on the 2.39 line.
Instance report events ¶
Issue 38483, commit 6174e8e, wires delivery outcome into the log streaming bus. InstanceReportingService.sendReport in packages/cli/src/modules/instance-reporting/instance-reporting.service.ts already POSTs to INSTANCE_REPORTS_PATH with ignoreHttpStatusErrors set and redirects disabled. Those HTTP rules stay. The patch emits an internal event on each existing branch.
A 201 emits instance-report-delivered. A 409 means a report with the same instance id and batch id is already recorded. That branch logs an error and does not throw, so it also emits instance-report-delivered, and the row is marked delivered. Every other status throws OperationalError with the text Instance report was rejected with status plus the code. The catch emits instance-report-failed, calls recordFailure, and rethrows. A transport error, the case the new unit test covers with Network error, emits only the failed event. The existing MAX_ATTEMPTS constant is still 3. The skip path after that budget is unchanged.
LogStreamingEventRelay in packages/cli/src/events/relays/log-streaming.event-relay.ts maps instance-report-delivered and instance-report-failed to EventMessageGeneric names n8n.instanceReporting.success and n8n.instanceReporting.failed. The map in packages/cli/src/events/maps/relay.event-map.ts types both internal events as empty objects. The comment there says the event name is the whole signal. instanceId, batchId, n8nVersion, dataPoints, and the optional label stay on the HTTP body.
A destination can alert without a payload. It cannot tell a 201 from a 409, or a timeout from a rejected status, without the process logs. The names are registered as eventNamesInstanceReporting and spread into eventNamesAll in packages/cli/src/eventbus/event-message-classes/index.ts. The locale key settings.log-streaming.eventGroup.n8n.instanceReporting is labeled Instance Reporting. A destination subscribed only to workflow or execution events stays quiet until it includes these two names.
First tick waits for server start ¶
InstanceReportingScheduler used to call start() from init whenever instanceSettings.isLeader was true. A boot catch up could emit before the event bus and its destinations were up, and the event had nowhere to land.
init now calls eventService.once('server-started', this.onServerStarted) on the leader. onServerStarted calls start(). stop(), marked @OnLeaderStepdown(), calls eventService.off('server-started', this.onServerStarted) before it clears the timeout. If leadership is lost before server-started, the listener is removed. It does not leak, it does not send a report, and a later takeover does not run that deferred tick a second time. A process that is not the leader still does not arm the scheduler. The interval constants in this file are untouched.
Event groups match on a dot boundary ¶
eventGroupsFromStringList grouped names with a raw startsWith on the group string. The name n8n.foobar starts with n8n.foo, so the shorter group swallowed the longer name. A new group whose name merely began with an existing group string would have been hidden the same way.
The filter in packages/frontend/editor-ui/src/features/integrations/logStreaming.ee/logStreaming.store.ts now keeps a name only when it equals the group, or when it starts with the group followed by a dot. logStreaming.store.test.ts pins the boundary: n8n.foo keeps n8n.foo.created, and n8n.foobar keeps n8n.foobar.created. Selecting Instance Reporting therefore lists n8n.instanceReporting.success and n8n.instanceReporting.failed on their own.
Revoke without a project scope ¶
Issue 38455, commit 3d8f578, is labeled editor in the release notes. The diff is only packages/cli/src/modules/dynamic-credentials.ee/dynamic-credentials.controller.ts and its tests. No editor UI file changes.
On a public chat or form trigger that uses n8n user auth, a user with no project role could connect an end user credential and then could not revoke it. The authorize route that builds the reconnect URL had the same block.
findCredentialToUse used to pass credential:update into credentialsFinderService.findCredentialForUser for a session user. Callers with no n8n user were loaded by id through enterpriseCredentialsService.getOne. Connect never required that scope. Disconnect and reconnect did.
The method now loads by id only. A missing row, or isResolvable not true, throws NotFoundError with the message Credential not found. One message covers both cases, so the route does not reveal whether the id exists. A fixed credential, whose token lives on the shared row, stays on the credential:update paths. The controller comment says the resolver keys every read and write on the caller identity, so a caller reaches only their own stored token.
Tests expect resolver.deleteSecret once and status 204, and they expect findCredentialForUser not to run. isResolvable set false rejects both authorize and revoke, and neither deleteSecret nor generateAOauth2AuthUri runs. The routes in the CORS fixtures are POST /credentials/:id/authorize and DELETE /credentials/:id/revoke.
The permission change is wider than the bug title. Any session user can now authorize or revoke a resolvable credential id, including the id someone-elses-credential from the unit test, with no project scope. The guard is isResolvable plus the resolver, not a check that the credential row belongs to the caller project. Operators who publish chat or form pages should treat those credential ids as reachable by any logged in end user.
Where to get it ¶
- Release page: n8n 2.39.5
- Repository: n8n on GitHub
- Tag:
[email protected]