Summary
The trigger creates a Hookdeck destination on activation and never deletes it. On
deactivation it pauses, disables or deletes the connection — the destination it provisioned
is always left behind. Destinations therefore accumulate permanently in a user's project, one
(two with the -test variant) per workflow-and-node, for the lifetime of the project.
This is user-facing, not just a test-suite problem. The live suite has the same blind spot in
its own cleanup, which adds to the pile — covered separately below.
Evidence
A real Event Gateway project used for development over nine days, snapshotted before cleanup:
destinations total : 54
node-provisioned : 53
referenced by a connection: 4
ORPHANED (no connection) : 49
accumulated between : 2026-08-12 and 2026-08-21
connections total : 10
49 of 53 node-provisioned destinations had no connection pointing at them. Nothing removes
them, and nothing surfaces them — they are invisible until you list destinations.
Two of them were the pair behind a single workflow:
n8n-dest-pQYxZBBy5lw1X2Ib-…-test type=CLI created 17:07:00 <- connection already gone
n8n-dest-pQYxZBBy5lw1X2Ib-… type=CLI created 17:06:55
Cause
webhookMethods.default.delete in nodes/Hookdeck/HookdeckEventGatewayTrigger.node.ts acts
only on the connection:
pause (the default for a production registration) — PUT /connections/:id/pause
disable (a test registration delivering over the CLI) — PUT /connections/:id/disable
delete (a test registration over HTTP, or onDeactivate: delete) — DELETE /connections/:id
grep -rn "destinations/" nodes/ | grep -i delete returns nothing. The node issues no
DELETE /destinations/:id anywhere.
For pause and disable that is correct — the connection survives, so its destination must
too. The leak is the delete path: the connection goes and its destination is orphaned
immediately.
Suggested fix
Delete the destination alongside the connection on the delete path only. Destination names
are derived per workflow-and-node (n8n-dest-<workflowId>-<nodeId>, plus the -test
variant), so they are 1:1 with the connection and safe to remove together — but that
assumption is worth asserting rather than trusting, since deleting a shared destination would
break another connection.
Points worth deciding:
- Ordering. Delete the connection first, then the destination; a destination still
referenced by a connection will refuse to delete.
- A 404 on the destination is success, exactly as the connection delete already treats it.
- Existing users have a backlog. A fix stops the bleeding but does not clear the 49
already there. Worth a note in the release, since the only remedy is manual, and possibly
worth a hookdeck gateway destination list one-liner in the README.
- Failure handling. The connection delete deliberately surfaces non-404 errors. A failed
destination delete is less serious — the connection is already gone, so nothing is
delivering — and arguably belongs as a warning rather than a thrown error, so teardown is
not blocked by it.
Related: the live suite has the same blind spot
cleanUpRun in test/live/_harness.mjs filters on ownedByThisRun, which matches three name
shapes:
name.startsWith(PREFIX) || name.startsWith(`cli-${PREFIX}`) || name.startsWith(`n8n-${RUN_ID}-`)
The node names destinations n8n-dest-<run>-<node>, which matches none of them, so every
node-provisioned destination survives the sweep. Measured across one run: sources 8 → 8 and
connections 9 → 9 (clean), destinations 27 → 39.
This is the same bug the function's own comment already records for connections:
The third was missing at first, so every node-provisioned connection survived cleanup and
then blocked its source from being deleted.
Fixed there, missed for destinations. Note the comment's warning that widening the filter
starts deleting real resources — the fix should add the n8n-dest-<RUN_ID>- shape
specifically, not a looser prefix.
Notes
Found while verifying #10 and confirmed while clearing the development project down. The 73
resources deleted during that cleanup were snapshotted first; the counts above come from that
snapshot. Not fixed in #10 — unrelated to it, and it touches deletion logic against a live
project, which deserves its own PR and its own live-suite assertion.
A good regression test: assert after a delete-path teardown that the destination is gone,
not just the connection. The current suite would pass today with the leak in place.
Summary
The trigger creates a Hookdeck destination on activation and never deletes it. On
deactivation it pauses, disables or deletes the connection — the destination it provisioned
is always left behind. Destinations therefore accumulate permanently in a user's project, one
(two with the
-testvariant) per workflow-and-node, for the lifetime of the project.This is user-facing, not just a test-suite problem. The live suite has the same blind spot in
its own cleanup, which adds to the pile — covered separately below.
Evidence
A real Event Gateway project used for development over nine days, snapshotted before cleanup:
49 of 53 node-provisioned destinations had no connection pointing at them. Nothing removes
them, and nothing surfaces them — they are invisible until you list destinations.
Two of them were the pair behind a single workflow:
Cause
webhookMethods.default.deleteinnodes/Hookdeck/HookdeckEventGatewayTrigger.node.tsactsonly on the connection:
pause(the default for a production registration) —PUT /connections/:id/pausedisable(a test registration delivering over the CLI) —PUT /connections/:id/disabledelete(a test registration over HTTP, oronDeactivate: delete) —DELETE /connections/:idgrep -rn "destinations/" nodes/ | grep -i deletereturns nothing. The node issues noDELETE /destinations/:idanywhere.For
pauseanddisablethat is correct — the connection survives, so its destination musttoo. The leak is the
deletepath: the connection goes and its destination is orphanedimmediately.
Suggested fix
Delete the destination alongside the connection on the
deletepath only. Destination namesare derived per workflow-and-node (
n8n-dest-<workflowId>-<nodeId>, plus the-testvariant), so they are 1:1 with the connection and safe to remove together — but that
assumption is worth asserting rather than trusting, since deleting a shared destination would
break another connection.
Points worth deciding:
referenced by a connection will refuse to delete.
already there. Worth a note in the release, since the only remedy is manual, and possibly
worth a
hookdeck gateway destination listone-liner in the README.destination delete is less serious — the connection is already gone, so nothing is
delivering — and arguably belongs as a warning rather than a thrown error, so teardown is
not blocked by it.
Related: the live suite has the same blind spot
cleanUpRunintest/live/_harness.mjsfilters onownedByThisRun, which matches three nameshapes:
The node names destinations
n8n-dest-<run>-<node>, which matches none of them, so everynode-provisioned destination survives the sweep. Measured across one run: sources 8 → 8 and
connections 9 → 9 (clean), destinations 27 → 39.
This is the same bug the function's own comment already records for connections:
Fixed there, missed for destinations. Note the comment's warning that widening the filter
starts deleting real resources — the fix should add the
n8n-dest-<RUN_ID>-shapespecifically, not a looser prefix.
Notes
Found while verifying #10 and confirmed while clearing the development project down. The 73
resources deleted during that cleanup were snapshotted first; the counts above come from that
snapshot. Not fixed in #10 — unrelated to it, and it touches deletion logic against a live
project, which deserves its own PR and its own live-suite assertion.
A good regression test: assert after a
delete-path teardown that the destination is gone,not just the connection. The current suite would pass today with the leak in place.