You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
HttpPluginRepository.prefetch() crashes startup on HTTP 429 — registry.nextflow.io/api/v1/plugins/dependencies called unconditionally since 26.04.2 #7176
Expected: When nextflow plugin install <plugin>@<version> is run and the registry returns HTTP 429 (rate limit), Nextflow should retry with backoff and/or degrade gracefully — it should not abort startup. Additionally, if all required plugins are already installed locally, the metadata prefetch network call should be skipped entirely.
Actual:HttpPluginRepository.sendAndParse() throws a hard PluginRuntimeException on any non-200 response including 429, crashing Nextflow startup unconditionally. With ~40 parallel CI jobs, registry.nextflow.io/api/v1/plugins/dependencies is reliably rate-limited and every job fails at startup.
Steps to reproduce the problem
Set up a CI system with 40+ parallel jobs, each running:
All jobs start within a short window (typical in matrix CI)
Every job calls registry.nextflow.io/api/v1/plugins/dependencies at startup via HttpPluginRepository.prefetch() — even if the plugin is already cached locally
The registry returns 429; Nextflow aborts with PluginRuntimeException
This is a regression from 25.04.3, which used DefaultUpdateRepository (static plugins.json) instead of HttpPluginRepository and did not make per-startup /api/v1/plugins/dependencies calls. Pinning back to 25.04.3 eliminates the failures.
The call chain with no 429 handling and no skip-if-installed logic:
1. PluginsFacade.groovy:415 — unconditional call, no if (!offline) guard:
updater.prefetchMetadata(startable)
offline is correctly read from NXF_OFFLINE at line 66 and passed to PluginUpdater at line 254, but never used to guard this call.
2. PluginUpdater.groovy:150–163 — iterates repos, calls prefetch() on each PrefetchUpdateRepository. this.offline is stored on the instance but never consulted here.
3. HttpPluginRepository.groovy:142–157 — builds and fires the HTTP request on every startup, regardless of whether the plugin is already installed:
def uri = url.resolve("v1/plugins/dependencies?plugins=...&nextflowVersion=...")
return sendAndParse(HttpRequest.newBuilder().uri(uri).GET().build())
4. HttpPluginRepository.groovy:165–167 ← THE BUG — hard-aborts on any non-200 including 429, with no Retry-After handling:
Bug report
Expected behavior and actual behavior
Expected: When
nextflow plugin install <plugin>@<version>is run and the registry returns HTTP 429 (rate limit), Nextflow should retry with backoff and/or degrade gracefully — it should not abort startup. Additionally, if all required plugins are already installed locally, the metadata prefetch network call should be skipped entirely.Actual:
HttpPluginRepository.sendAndParse()throws a hardPluginRuntimeExceptionon any non-200 response including 429, crashing Nextflow startup unconditionally. With ~40 parallel CI jobs,registry.nextflow.io/api/v1/plugins/dependenciesis reliably rate-limited and every job fails at startup.Steps to reproduce the problem
registry.nextflow.io/api/v1/plugins/dependenciesat startup viaHttpPluginRepository.prefetch()— even if the plugin is already cached locallyPluginRuntimeExceptionThis is a regression from 25.04.3, which used
DefaultUpdateRepository(staticplugins.json) instead ofHttpPluginRepositoryand did not make per-startup/api/v1/plugins/dependenciescalls. Pinning back to 25.04.3 eliminates the failures.Program output
Root cause (specific code pointers, v26.04.2)
The call chain with no 429 handling and no skip-if-installed logic:
1.
PluginsFacade.groovy:415— unconditional call, noif (!offline)guard:updater.prefetchMetadata(startable)offlineis correctly read fromNXF_OFFLINEat line 66 and passed toPluginUpdaterat line 254, but never used to guard this call.2.
PluginUpdater.groovy:150–163— iterates repos, callsprefetch()on eachPrefetchUpdateRepository.this.offlineis stored on the instance but never consulted here.3.
HttpPluginRepository.groovy:142–157— builds and fires the HTTP request on every startup, regardless of whether the plugin is already installed:4.
HttpPluginRepository.groovy:165–167← THE BUG — hard-aborts on any non-200 including 429, with noRetry-Afterhandling:Environment
Additional context
NXF_OFFLINE=true. That workaround is incomplete: it requires plugins to be pre-cached locally and prevents any network access, making it incompatible with steps that legitimately need to install plugins.sendAndParse(): handle 429 specifically — respectRetry-Afterheader, retry with exponential backoff, don't hard-abort.prefetchMetadata/prefetch(): skip the network call if all requested plugins are already present locally.if (!offline)guard atPluginsFacade.groovy:415.