fix: bundle Slack runtime deps to make community-node install deterministic - #15
fix: bundle Slack runtime deps to make community-node install deterministic#15kilpa wants to merge 1 commit into
Conversation
…nistic
n8n installs community packages with `--install-strategy=shallow`, which
disables npm's normal dependency hoisting and forces transitive deps into
deeply nested per-package node_modules trees. In some npm/cache states
this produces a nested copy of `@slack/socket-mode` under `@slack/bolt`
whose `dist/` is not extracted, causing n8n's package loader to fail
with:
ENOENT: no such file or directory, open
'.../n8n-nodes-slack-socket-mode/node_modules/@slack/bolt/node_modules/@slack/socket-mode/dist/src/index.js'
After the failure, n8n's lifecycle service auto-uninstalls the package,
making the node permanently unusable for affected users.
Listing the runtime Slack and proxy dependencies in `bundleDependencies`
ships them pre-extracted inside the published tarball, so npm does not
need to resolve them transitively at install time. The install becomes
deterministic regardless of npm's install strategy or cache state.
Verified by packing locally and installing the resulting tarball with
the exact flags n8n uses (`--bin-links=false --install-strategy=shallow
--ignore-scripts=true --package-lock=false`) inside an n8n container:
`@slack/socket-mode/dist/src/index.js` is present and both `bolt` and
`socket-mode` can be required by the loader.
Fixes mbakgun#14
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Hi Hendrik, have you attempted building this package locally and then launching n8n using Docker Compose? Once built, if you mount the package into the container at install time (for instance via a volume), does everything function as expected? Thanks again for your support and for putting together this PR. |
|
Yes — verified with your Local-dev flow (volume-mounted package, your standard reproduction): Boot logs: Then inside the running container: So with the volume-mounted dev setup, the node loads, instantiates, and the bundled Note on what this does and doesn't exercise The dev flow above mounts The I also installed the resulting tarball on a real n8n queue-mode deployment (n8n 2.16.1, GKE, persistent PVC for the main pod). The package loaded cleanly on boot, no auto-uninstall, and the trigger is selectable in the editor. Happy to also bump the version (e.g. |
|
Hi @kilpa , I’ve published version 1.7.1, but I wasn’t able to upgrade from the previous 1.7.0 in my local environment. A fresh install is also failing. In other words, uninstalling and reinstalling(with the latest version) doesn’t work either, since 1.7.1 fails to install as well. Could you test this on your side? Based on your results, we can decide how to proceed with the pull request. |
|
hi @mbakgun, can confirm we are having the same problem as you described with v1.7.1 let me know what you think! |
|
@mbakgun with my changes everything seems to be installing fine with the latest n8n currently available. |
Very interesting, we had this issue on 2 different instances while upgrading from 1.7.0 to 1.7.1, and it looks like a similar situation happened on @SkybuIIy as well. By the way, in practice, the following is already the safe path and always works: remove the plugin from n8n, restart n8n, then install version 1.7.0 or earlier. There is no issue with this approach. |
I think it’s worth trying. Could you please open a separate pull request for it so that we can release version 1.7.2 and test it out? |
Summary
Fixes #14.
n8n installs community packages with
--install-strategy=shallow, which disables npm's normal dependency hoisting and forces transitive deps into deeply nested per-packagenode_modulestrees. Under that install strategy, in some npm/cache states a nested copy of@slack/socket-modeends up under@slack/bolt/node_modules/without itsdist/extracted, and n8n's package loader fails with:n8n's lifecycle service auto-uninstalls the package after the load failure, so the node is permanently unusable for affected users until the install path itself is made deterministic.
Change
Add
bundleDependencieslisting the four runtime deps (@slack/bolt,@slack/socket-mode,http-proxy-agent,https-proxy-agent) so they are shipped pre-extracted inside the published tarball. npm then does not need to resolve them transitively at install time, and the install becomes deterministic regardless of install strategy or npm cache state."dependencies": { "@slack/bolt": "^4.6.0", "@slack/socket-mode": "^2.0.5", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6" - } + }, + "bundleDependencies": [ + "@slack/bolt", + "@slack/socket-mode", + "http-proxy-agent", + "https-proxy-agent" + ] }Tradeoffs
dependencies(npm requires this when usingbundleDependencies), so semver and Dependabot continue to work as before. Bumping a Slack dep is a one-line change followed bynpm install+npm pack.dist/via esbuild/tsup; that produces a smaller tarball but is a larger refactor.bundleDependenciesis the minimum-diff fix that solves the underlying install determinism problem.Verification
Packed the branch locally and installed the resulting tarball with the exact flags n8n uses inside an n8n container (
node 24.14.1,npm 11.12.1):Result:
@slack/socket-mode/dist/src/index.jsis present at the expected path, and both@slack/boltand@slack/socket-moderequire()cleanly under the package root.Notes
1.7.1(or whatever you'd like) before publish.Disclosure: this PR was prepared with the help of Claude (issue investigation, repro, and writeup). The diff is small and reviewable; happy to iterate.