Skip to main content
← Back to list
01Issue
BugShippedSwamp CLI
Assigneeskeeb

Relationships

#424 swamp serve: extension bundle failure at startup is silently pinned, breaking scheduled workflows with permanent 'Unknown model type'

Opened by keeb · 5/23/2026· Shipped 5/23/2026

Summary

When swamp serve's eager extension load at daemon startup fails to bundle an extension, the failure is swallowed, recorded as a sticky BundleBuildFailed catalog entry, and never retried — across daemon restarts and even across later successful online runs. Scheduled (and WebSocket) workflow execution then fails permanently with Unknown model type: <type>, while CLI swamp workflow run can still resolve the type if its bundle was warmed first. This makes a freshly-deployed swamp serve appear to "lose" a model type that the CLI resolves fine on the same machine, repo, user, and binary.

Environment

  • swamp 20260521.221703.0-sha.3f2b75f8 (also reproduced deterministically on current main, 8ba74414)
  • Linux, systemd-managed swamp serve running as root (vanilla unit: WorkingDirectory, ExecStart=swamp serve --repo-dir ... , Environment=NO_COLOR=1; no resource limits or sandboxing)
  • A model extension importing an npm dependency (e.g. npm:nodemailer), a cold deno cache, and no/blocked outbound network to npmjs.org at first bundle

Steps to reproduce

  1. Fresh repo. Add a model extension importing an npm dep, e.g. import nodemailer from "npm:nodemailer@6.9.16";, type @user/foo/bar.
  2. Ensure no prior bundles exist: .swamp/bundles/ and .swamp/_extension_catalog.db absent (fresh deploy, before any interactive CLI run bundled anything).
  3. Create a model definition and a workflow with trigger.schedule: "* * * * *" invoking a method on it.
  4. Start swamp serve with a cold deno cache and no outbound network to registry.npmjs.org (e.g. a freshly-provisioned VM whose npm/deno cache was never warmed and/or whose service has blocked egress).
  5. Wait for the cron to fire.

Expected

Either the extension bundles (when network is available) and the type resolves, OR the daemon surfaces a loud, actionable error that the extension could not be bundled — and retries when conditions change (network restored, restart).

Actual

  • deno bundle fails fetching npm deps from registry.npmjs.org (tcp connect error: Network is unreachable).
  • The failure is swallowed; the catalog records BundleBuildFailed with an empty type_normalized; the type is excluded from registration.
  • The scheduled run throws Unknown model type: @user/foo/bar.
  • The failure is sticky: re-bundling only happens on a source fingerprint mismatch, so a BundleBuildFailed row whose fingerprint still matches is never retried. Restarting the service does not clear it, and even a later online CLI run on the same repo does not recover it.

Why the CLI "works" but serve doesn't

Ordering. Whoever bundles first while online wins. If an interactive CLI warmed .swamp/bundles/ before serve started, serve reuses the cache and resolves fine. On a fresh deploy where serve starts first on a cold cache with no egress, the failure is pinned — the operator then sees the CLI succeed (its run used/warmed good bundles) while the long-running daemon stays broken across restarts.

Root cause (code)

  1. src/domain/models/bundle.ts:387-406bundleExtension spawns deno bundle, which must fetch npm deps from the network when the deno cache is cold; no offline / --cached-only guard.
  2. src/serve/deps.ts:66-71 — serve does a one-shot eager modelRegistry.ensureLoaded() at daemon startup, bundling the whole extension suite.
  3. src/domain/extensions/extension_loader.ts:342-373 and :704-713 — bundle failures are caught, recorded as BundleBuildFailed, and excluded from lazy registration.
  4. src/domain/extensions/bundle_freshness.ts:307-321findStaleFiles only re-bundles on a fingerprint mismatch, so a fingerprint-matching failed entry is pinned across restarts and later online runs.
  5. src/domain/workflows/execution_service.ts:469resolveModelType returns undefined and throws Unknown model type.

Suggested fixes

  • Do not treat BundleBuildFailed as a satisfied fingerprint-cache entry — retry failed builds (on restart at minimum).
  • Fail loud at serve startup when an extension cannot be bundled, instead of silently dropping the type.
  • Offline/cold-cache handling: pre-warm bundles, support --cached-only semantics, or surface a clear "could not fetch npm deps to bundle X" error to the operator.
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 3 MOREPR_MERGED+ 1 MORENOTIFICATION_SKIPPED

Shipped

5/23/2026, 10:11:19 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
keeb assigned keeb5/23/2026, 8:54:38 PM

Sign in to post a ripple.