Relationships
Blocked#1974 Every swamp command dies at module load when $TMPDIR sits under a package.json declaring "type": "commonjs"
Opened by jpisgeek · 9/3/2026
Description
The compiled binary aborts before executing any command:
error: Uncaught SyntaxError: The requested module 'fzf' does not provide an export named 'Fzf'
import { Fzf, type FzfResultItem } from "fzf";
^
at file:///private/tmp/deno-compile-swamp/src/presentation/renderers/components/search_picker.tsx:23:10Exit code 1. This is not confined to the interactive search commands. Every
command class I tried fails identically, because search_picker.tsx sits in the
statically-imported module graph reachable from the entry point:
| command | with trigger present | control |
|---|---|---|
swamp version |
SyntaxError, rc=1 | OK |
swamp help |
SyntaxError, rc=1 | OK |
swamp doctor extensions |
SyntaxError, rc=1 | OK |
swamp model search |
SyntaxError, rc=1 | OK |
swamp workflow search |
SyntaxError, rc=1 | OK |
swamp vault list |
SyntaxError, rc=1 | OK |
swamp data list <model> |
SyntaxError, rc=1 | OK |
The user-visible symptom is that swamp appears completely and permanently broken, with an error naming an internal renderer component and no hint that the environment is involved.
Impact
Total loss of the CLI, with no degraded mode. A user in this state cannot run
swamp version to identify their build, cannot run swamp doctor to diagnose
it, and cannot run swamp update to escape it. The documented first-line
troubleshooting step in the troubleshooting skill (swamp update --check) is
itself unavailable, so the CLI cannot help diagnose its own failure.
This is easy to fall into on macOS. /private/tmp is an ordinary TMPDIR
value, and any npm init or npm install run in a /tmp scratch directory
leaves a package.json behind. Verified locally: npm init -y on npm 11.19.0
writes "type": "commonjs" into the scaffold unprompted. So the trigger gets
created without the user doing anything unusual, and it persists across reboots.
In our case the binary ran a workflow successfully at 03:06 local and failed at
03:27 with the file byte-identical and untouched (mtime 2026-09-03 02:54:44,
sha256 confirmed unchanged before and after). Nothing about swamp changed; only
the shell's TMPDIR differed between the two sessions. It reads convincingly as
a corrupted install, and we nearly reinstalled on that assumption.
Steps to Reproduce
Minimal, from a working swamp:
mkdir -p /var/tmp/swrepro
printf '{"type":"commonjs"}' > /var/tmp/swrepro/package.json
TMPDIR=/var/tmp/swrepro swamp version # -> SyntaxError, exit 1
TMPDIR=/var/tmp swamp version # -> worksThe package.json does not need to be in $TMPDIR itself. A parent directory
is enough:
mkdir -p /var/tmp/swanc/sub
printf '{"type":"commonjs"}' > /var/tmp/swanc/package.json
TMPDIR=/var/tmp/swanc/sub swamp version # -> SyntaxError, exit 1Analysis
deno compile roots the binary's virtual filesystem at
$TMPDIR/deno-compile-swamp, which is visible in the error's module URL. Deno's
CommonJS/ESM format detection then walks the real filesystem upward from
that path looking for the nearest package.json, finds the unrelated one in the
user's temp directory, and honours its "type": "commonjs". The embedded fzf
ESM module is consequently parsed as CommonJS, its named exports are not
statically visible, and import { Fzf } fails.
The bundled npm snapshot is correct and intact. The manifest embedded in the
binary pins "fzf": "npm:fzf@0.5.2", which does export Fzf. The package is
not being replaced, it is being misclassified.
Confirmed by elimination, each on a freshly created directory:
$TMPDIR |
nearest ancestor package.json |
result |
|---|---|---|
| unset | — | works |
/var/tmp |
none | works |
$HOME/tmpprobe |
none | works |
| fresh dir, no package.json | none | works |
fresh dir + {"type":"module"} |
type module | works |
fresh dir + {"name":"x"} |
no type field | works |
fresh dir + {"type":"commonjs"} |
type commonjs | fails |
/private/tmp (npm-generated) |
type commonjs | fails |
/tmp (same inode as above) |
type commonjs | fails |
Two plausible hypotheses were tested and ruled out, recorded so nobody retries them:
- A stray
node_modulesin the temp directory. One does exist on this machine at/private/tmp/node_modules, but planting an equivalent under a cleanTMPDIRdid not reproduce the failure, and removing it did not change the result. - A stale or corrupt Deno cache. Running with
DENO_DIRpointed at an empty directory reproduced the failure identically.
Suggested fixes
Ordered most valuable first:
- Make the compiled binary's module-format resolution ignore
package.jsonfiles outside its own virtual filesystem. The embedded graph's format is known at compile time and should not be re-derived from whatever happens to sit above$TMPDIRon the host. - Failing that, place the VFS root somewhere user scratch files cannot shadow
it — a swamp-owned directory such as
~/.swamp/run/, or a randomised subdirectory — rather than directly under$TMPDIR. - At minimum, catch module-load failures at the entry point and emit an
actionable message naming
$TMPDIRand the offendingpackage.json, so thatswamp versionandswamp doctorsurvive to report the problem.
Environment
- swamp
20260903.004401.0-sha.f2eefb58.swamp update --checkreports up to date, so there is no newer build to escape to. - Installed at
~/.swamp/bin/swamp, symlinked from~/.local/bin/swamp, 304212866 bytes, mtime2026-09-03 02:54:44 -0400. - macOS 26.6.2 (build 25G83), arm64.
- Login shell exports
TMPDIR=/private/tmp. - Offending file:
/private/tmp/package.json, annpm initleftover dated 2026-08-30, containing"type": "commonjs".
Workaround
Point TMPDIR at a directory with no "type": "commonjs" ancestor, or remove
the stray package.json:
TMPDIR=/var/tmp swamp <command>Closed
No activity in this phase yet.
jpisgeek commented 9/3/2026, 11:12:58 AM
Second sighting of the #1974 failure shape on the same machine, different
trigger, same architectural cause: work done at module-import time turns an
unrelated environmental fault into total loss of the CLI, swamp version and
swamp doctor included.
Being upfront about ownership: the fault that triggered this is mine, not
yours. My host's keychain was wedged by an unanswered SecurityAgent
authorization prompt. That part is not a swamp bug and I am not asking you to
fix it. I am filing because of how swamp reacted to it.
Symptom
Every command, rc=1, no output:
error: Uncaught (in promise) Error: Failed to load platform certificates: [REDACTED-SECRET-1]
const wsHttpClient = Deno.createHttpClient({});
^
at file:///var/tmp/deno-compile-swamp/src/cli/remote_run.ts:423:27Confirmed against version, help, auth status, model search, and
issue search. Independent of $TMPDIR, so distinct from #1974's trigger.
The clean isolation
This is the part worth your time. The failure is specific to the system trust store:
| result | |
|---|---|
DENO_TLS_CA_STORE=mozilla swamp version |
works |
DENO_TLS_CA_STORE=system swamp version |
fails |
DENO_TLS_CA_STORE=mozilla,system swamp version |
fails |
Network and TLS were healthy throughout — curl https://swamp-club.com
returned HTTP 200 in 0.4s while swamp could not start. So this is not
connectivity, it is the platform trust store load specifically. Anyone stuck
here can escape with DENO_TLS_CA_STORE=mozilla, which is a far better
answer than the reinstall I nearly performed.
For completeness on the host fault: SecurityAgent pid 82972 alive 6h37m,
first blocked keychain client one second after it spawned, security list-keychains and find-certificate hanging on a 12s timeout while
security default-keychain returned instantly.
Why it is still a swamp-side issue
swamp version does not need an HTTP client and swamp --help does not need
the platform trust store. Both die anyway, because Deno.createHttpClient({})
runs at module scope in src/cli/remote_run.ts, inside the eagerly-imported
graph. It surfaces as an unhandled promise rejection rather than a handled
error, so there is no diagnostic path and no actionable message.
That is exactly #1974: there, an eager import of search_picker.tsx propagated
an fzf format-detection failure into every command. Here an eager
createHttpClient propagates a platform-trust failure into every command. Two
unrelated environmental faults, one architectural cause, and both times the
casualties include the self-diagnosis commands your own troubleshooting skill
tells users to reach for first.
Suggested fix
Extends suggestion 3 on #1974:
- Defer HTTP client construction to first use rather than module scope. No command that makes no network call should be able to die on certificate loading.
- Audit the eagerly-imported graph for other import-time side effects. Both faults found so far were reached from commands that had no business touching them.
- Surface startup failures as handled errors with actionable text, not uncaught
promise rejections. At minimum
versionanddoctorshould survive any environmental fault.
Environment
- swamp
20260903.004401.0-sha.f2eefb58,update --checkreported up to date before the fault began - macOS 26.6.2 (build 25G83), arm64
Two different gators bit me in the same swamp today and neither one was yours — but both times swamp went under with me instead of throwing me a rope. That rope is all I am asking for.
jpisgeek commented 9/3/2026, 11:13:38 AM
Correction to my previous comment, and an unintended bug report.
Your secret-redaction filter ate the most important token in it. The line now reads:
error: Uncaught (in promise) Error: Failed to load platform certificates: [REDACTED-SECRET-1]What belonged there is a public Apple API symbol, not a secret: SecTrustSettings
immediately followed by CopyCertificates, from Security.framework. Written as
one word it apparently looks enough like a credential to get scrubbed. I have
split it here so it survives.
The CLI did tell me it had redacted something, so nothing was hidden from me — credit where due. But the practical effect is that a bug report loses the exact symbol a maintainer would grep for, and the author only finds out if they read the JSON. Worth a look at the false-positive rate on long camelCase identifiers, since stack traces and API names are exactly what bug reports are made of.
No secret was present in that comment, or in this one.
I came here to report one thing and stepped on two more on the way out. Very on-brand for a swamp.
stack72 commented 9/4/2026, 3:53:33 PM
This is blocked right now - it's been isolated to an upstream deno issue - https://github.com/denoland/deno/issues/36771
stack72 commented 10/8/2026, 9:12:18 PM
Closing this out as we are waiting on a fix upstream here
Sign in to post a ripple.