Relationships
#214 Port importBundleByPath ENOENT fallback to datastore/driver/vault/report loaders
Opened by keeb · 5/2/2026
Summary
Follow-up to swamp-club#212 / PR systeminit/swamp#1288. The fix in #1288 added two layers of defense against missing cached bundle files:
- A bundle-existence gate in
findStaleFiles(domain-shared — all six loaders inherit it viabundle_freshness.ts). - An ENOENT fallback in
UserModelLoader.importBundleByPaththat rebundles from source on demand.
Layer 1 covers all six loaders. Layer 2 (the import-site fallback) was only added to UserModelLoader. The four sibling loaders — UserDatastoreLoader, UserDriverLoader, UserVaultLoader, UserReportLoader — each have their own importBundle / loadSingleType paths that read entry.bundle_path directly with no ENOENT recovery. They benefit from layer 1 transparently, but they still lack the import-site defense for two cases:
- Concurrent-rm races: a
swamp data gcor externalrmbetweenfindStaleFilesand the loader's import call can ENOENT. - Hot-load attach paths analogous to
UserModelLoader.attachPendingExtensionsForTypethat may bypass freshness in some flows.
Reproducer
Same pattern as swamp-club#212, but targeting a sibling kind:
- Define a custom datastore (or driver/vault/report) under the appropriate
extensions/path. - Run any command that loads it so the catalog and bundle exist.
- While that command is still resolving the type,
rmthe bundle file out from under it (or rely onswamp data gcto misfire). - The catch path will throw NotFound from
Deno.readTextFile(entry.bundle_path)instead of recovering.
The buildIndex path is fully covered by the layer-1 gate, so this is the residual surface only — narrow but a real hole for racy operations.
Suggested fix
Apply the same surgical pattern to each sibling loader's importBundle-equivalent:
- Catch
Deno.errors.NotFoundfromDeno.readTextFile(entry.bundle_path). - Rebundle from source via the loader's existing
bundleExtensioncall (each loader already has one for first-load and stale-rebuild paths). - Write the rebundled JS back to
entry.bundle_pathand continue with the import. - Document the single-import-per-process invariant (per #1288's comment).
Each sibling loader has its own recoverMissingBundle shape candidate. Could also consider hoisting the helper into a shared utility — but the loaders' bundle paths and helpers diverge enough that copy-paste is probably cleaner than premature abstraction.
Environment
- swamp main as of
v20260502.153639.0-sha.907c6883(the merge that landed #1288) - Affects all four sibling loaders symmetrically — Linux, macOS, Windows
Closed
No activity in this phase yet.
stack72 commented 5/4/2026, 4:48:37 PM
Strategic fix lands in W4 of the extension catalog rearchitecture (see #211 for sequence context). This issue is the same "we forgot to do this in four places" pattern as #128 and #209 — duplication across the five user-loader files. W4 collapses the five loaders into a single `KindAdapter`-parameterized path, at which point `importBundleByPath`'s ENOENT fallback (added for models in #212 / PR #1288) becomes one code path covering all five kinds, closing this issue.
Tactical port to the four siblings is not currently planned — porting the fix four times now means deleting four code paths when W4 lands. W4 is several weeks out (third dependent workstream after W1a/W1b and W2/W3), not months.
If you're actively hitting this on a non-model extension (vault, driver, datastore, or report), please comment with the extension and the failure shape. Active user impact is the bar for expediting a tactical port. Absent that signal, this issue stays open as a tracking record and the ENOENT fallback is incorporated into W4's design as a baseline requirement.
Sign in to post a ripple.