Repository navigation
REGRESSION: POST /amicode/connections/credential (pasqal-cloud) segfaults Bun — bisected to 467fb15cf #82
Description
Rewritten. The first version of this issue concluded "latent Bun bug, skip the test." That was wrong. The test passed six days ago and a specific commit broke it. Details below.
Not an OOM
The unit (linux) job dies with exit code 137 and turbo labels it "likely due to running out of memory." That label is boilerplate turbo prints for any signal-killed child. Memory is not involved:
Elapsed: 1498ms | RSS: 0.15GB | Peak: 0.37GB | Faults: 0 | Machine: 32.38GB
panic(main thread): Segmentation fault at address 0x0
0.37 GB peak on a 32 GB machine, zero faults, dead in 1.5 s. SIGILL is Bun's crash handler trapping after the panic; 137 is turbo relaying it.
It is a regression, not latent debt
Test: test/server/amicode-connections-routes.test.ts:335 —
"full lifecycle: submit(valid) → connected + devices; revalidate never respawns; disconnect scrubs"
The test file is unchanged since 2026-07-20. Bun has been pinned at 1.3.14 since 2026-05-18. Only connections.ts moved underneath it. Bisected by running that one test at each commit that touched connections.ts:
| commit | date | result |
|---|---|---|
85922e176 |
2026-07-20 | 1 pass |
3e4b37298 |
2026-07-21 | 1 pass |
467fb15cf |
2026-07-21 | CRASH |
8c6dd6940 |
2026-07-27 | CRASH |
f43233e68 (tip) |
2026-07-27 | CRASH |
467fb15cf — "feat: two-step Pasqal auth — username+password → project picker (amicode#194)" is the first bad commit. It rewrote runPasqalValidator / submitPasqalCredential (+207 lines in connections.ts).
Localized to one statement
Inserting an early return at successive points in the test body:
| stop after | result |
|---|---|
const server = app() |
pass |
const stub = stageStubValidator() |
pass |
stub.scenario(0, validatorLine()) |
pass |
the POST /amicode/connections/credential + .json() |
CRASH |
So the crash is inside the credential-submit route handler for id: "pasqal-cloud". The sibling test that submits id: "company-compute" passes — that path uses an HTTP stub, not the spawn path. Every other test in the file passes individually; skipping this one alone yields 553 pass / 3 skip / 0 fail across all 63 files in test/server/.
Ruled out
- OOM — see above.
- A larger runner — memory was never the constraint. Also unavailable: org is
plan=free,GET /orgs/harmoniqs/actions/hosted-runners→404 GitHub hosted runners are not supported for this organization. - Upgrading Bun —
bun-v1.3.14is the current release;packageManageralready pins it and upstreamsst/opencodepins the same. returninsidetry/finally—467fb15cfrefactoredrunPasqalValidatorfrom assign-then-return intoreturndirectly insidetrywith a mutatingfinally. Hoisting it back out does not fix the crash. Tested.- bun-spawning-bun by itself — the harness sets
AMICO_PYTHON = process.execPath, so the realspawnPasqalValidatorspawns bun to run a generated.mjs. A standalone 20-iteration bun-spawns-bun-mjs test passes cleanly. The crash needs the Effect/route context.
Open — and it gates the amicode.10 tag
Unknown whether production is affected. The test drives the real spawn path with bun standing in as the interpreter; production spawns a real Python interpreter. So this may be a Bun bug reachable only under the test's interpreter substitution, or it may be a genuine fault in the rewritten submit path that Python would also hit. Not yet determined.
Until that is settled, v1.17.3-amicode.10 should not be cut on the assumption this is test-only.
cc @kate — 467fb15cf is yours; you likely have the fastest read on what changed in the submit path.
Found while establishing a CI baseline for #2 / #79 — this went unnoticed for 6 days because the fork's workflows request blacksmith-4vcpu-* runners the org has none of, so no test job has ever run here.
Activity
[-]unit suite dies with exit 137 — one test segfaults Bun 1.3.14 (not OOM)[/-][+]REGRESSION: POST /amicode/connections/credential (pasqal-cloud) segfaults Bun — bisected to 467fb15cf[/+]jack-champagne commentedon Jul 28, 2026
Update: reproduces with a REAL python3 — this is production-relevant
The earlier version of this issue left open whether the crash was an artifact of the test harness substituting bun for the Python interpreter (
AMICO_PYTHON = process.execPath). It is not.I rewrote
stageStubValidator()'s stub from a bun-executed.mjsinto an equivalentpython3-executed.py(same record/scenario contract) and setAMICO_PYTHON = "python3"— i.e. the real interpreter, as production uses:Still crashes. The interpreter was never the variable.
Control — the python3 stub is sound
Four other tests in the file, run against the same new python3 stub, all pass:
needs-key → submit → connected → revalidate → disconnect …null token over the route → session-only connected (AC4)offline boot over the route …expiry over the route: a past-expiry claim …So the stub spawns, records, and exits correctly. The fault is specific to the one test.
Narrowed: it is the SUCCESS path of the rewritten submit
Three tests post a full
pasqal-cloudcredential ({ id: "pasqal-cloud", ...PASQAL }):full lifecycle)scenario(0, validatorLine())— exit 0, full valid line: devices + mintable token + expiryfailure classes)null token)The only crashing case is the one where the validator succeeds with a complete payload — devices list plus a token to mint. That is precisely the path
467fb15cfrewrote (project selection → token extraction → device list).Also ruled out
process.env.AMICODE_ROUTE_CANARY— removing it does not stop the crash.returninsidetry/finally— hoisting it out does not stop the crash.Consequence
v1.17.3-amicode.10should not be cut until this is understood. The crash is on the production submit path with the production interpreter, reachable by any user who successfully authenticates a Pasqal account. Whether a realpasqal_validate.pyagainst the live SDK also trips it is untested, but there is no longer any reason to assume the harness is what makes it crash.The failing test is doing its job and should stay unskipped.
jack-champagne commentedon Jul 28, 2026
Confirmed against Pasqal production with real credentials — network ruled out
Ran the route against a real Pasqal login, the real
pasqal_validate.py, and the realpasqal-cloudSDK. It segfaults.3/3 consecutive runs, tightly clustered:panic: Segmentation fault at address 0x0panic: Segmentation fault at address 0x0panic: Segmentation fault at address 0x0Machine has 32 GB. Not memory, not intermittent.
The network hypothesis is dead
Suspected a connectivity flake. Ruled out three ways:
apis.pasqal.cloud(the endpoint_account_api_url()resolves to) answers in 0.51 s._is_network_error→EXIT_NETWORK_FAILURE, and the route's failure classes are covered and pass. A run with deliberately-bad credentials cleanly returnsstate=invalidin 2.8 s without crashing.So the interpreter, the SDK, the network and the validator are all healthy and produce a complete valid payload. The crash is in this repo's handling of that payload.
Note the timing gradient: a rejected login survives at 2.8 s; a successful one dies at ~3.7 s. It crashes after auth succeeds, devices are listed and the token is minted.
The stub was faithful after all
The stub's
validatorLine()emitsdevices: ["EMU_FREE", "FRESNEL"]— a list of strings; the real validator emits["FRESNEL", "FRESNEL_CAN1"], also a list of strings. No shape mismatch. Consistent with the crash signature being byte-identical between the real validator and the python3 stub (lt10d9b296inEukogC4j+hvE+1vRA2AA). The only unfaithful part was the interpreter (AMICO_PYTHON = process.execPath), and that produced a slightly different signature.Bottom line
467fb15cfintroduced a crash on the production Pasqal submit success path. Any user who successfully authenticates a Pasqal account takes down the opencode server process. This is not a test artifact and not environmental.v1.17.3-amicode.10must not be cut until this is fixed.Side effect worth checking
Because the process dies mid-submit, cleanup never runs and a freshly minted Pasqal token is left written to disk while the connection state is never reconciled. Worth checking what the connections panel reports after a crashed submit, and whether a real user ends up with a persisted credential and no connected state.
cc @kate
jack-champagne commentedon Jul 28, 2026
Root cause: the native keychain write
The segfault is at
connections.ts:1005:which reaches
@napi-rs/keyring's nativeEntry.setPassword()viakeychainSecretStore.write().Evidence
Instrumented the success path and ran the hermetic test:
[T4] after keychain writenever prints.Installing the in-memory store removes the crash entirely — the test then fails on an ordinary assertion instead of segfaulting:
Why this explains the exact pass/fail pattern
pasqalSecretStore().write()sits only on theoutcome.kind === "valid" && outcome.token !== nullbranch:full lifecycle(valid + token)valid+ tokennull token → session-onlyvalid, null tokenfailure classes(exit 2/4/1)config/invalidThat is the whole mystery: only one branch touches the native keyring.
467fb15cfis the first bad commit because it introduced that write — the ADR 0001 addendum for #194, putting the password in the OS keychain so the ~24 h token can be silently re-minted.It needs the route context
The keyring call is not crash-prone on its own. Both of these pass:
@napi-rs/keyringsetPasswordunderbun run— finebun testcase — fineIt only segfaults inside the Effect HttpApi route handler, after
writeCredential. So this is a narrow Bun/NAPI-under-Effect interaction, not "keyring is broken."Correction to my earlier comment
I previously wrote that "any user who successfully authenticates a Pasqal account takes down the opencode server process." That is not established and I withdraw it.
I tried to confirm it on the real server path —
bun run … serveon port 14099, real credentials, POST to the route. The server survived, but returnedstate=invalid: authentication failed, so execution never reached the keychain write. Inconclusive, not a pass.So the confirmed scope today is: the crash reproduces in the test runner, at the keychain write. Whether the compiled production binary crashes at the same site is still unverified. It needs a re-run once credentials authenticate again (see below).
Two separate test defects worth fixing regardless
amicode-connections-routes.test.tshas no keychain isolation. Its siblingamicode-connections.test.tsdoessetPasqalSecretStore(inMemorySecretStore())inbeforeEach; the routes test does not. So it drives the real OS keychain on the developer's machine. (The realpasqal-cloud/defaultslot reads as absent, so nothing was clobbered — the crash happens during the write, before it persists.)AMICO_PASQAL_KEYCHAIN_SERVICEis not in that test'sENV_KEYS.pasqal-secret.tsdocuments it as existing precisely "so an isolated sandbox / test run gets its own slot and never shares the real connection's secret." Unused here.Either mechanism fixes the crash. The seam is the better choice — it keeps the test hermetic with no keyring daemon dependency at all.
Note on an artifact in my reproduction
After installing the seam, the test fails on:
That is mine, not a product bug — I had swapped the stub validator from a bun-executed
.mjsto apython3-executed.py, and CPython synthesizesLC_CTYPEintoos.environ. Any real fix should assert against the interpreter's actual env surface, or filter locale vars.jack-champagne commentedon Jul 28, 2026
Production path does NOT crash — retracting the release block
I previously said
v1.17.3-amicode.10must not be cut. That was wrong. I retract it.Ran the real route on a real server with working credentials:
And it demonstrably reached the crash site. Two independent confirmations that execution passed through
connections.ts:1005:username len: 17,password len: 15(an isolated slot viaAMICO_PASQAL_KEYCHAIN_SERVICE, since deleted).pasqal.jsonwith['expires_at','project_id','token'],token_len: 1278.So
pasqalSecretStore().write()executed against the real native keyring, on the real success branch, and the server survived.Corrected conclusion
The segfault is confined to the
bun testrunner. It is not reachable in production.connections.ts:1005bun test+ Effect routebun run … serve+ Effect routebun test, keyring call standalonebun run, keyring call standaloneOnly the first combination crashes. So this is a Bun-test-runner/NAPI interaction, not a product fault —
467fb15cfis not shipping a crash to users.What the actual defect is
amicode-connections-routes.test.tshas no keychain isolation, so it drives the developer's real OS keyring insidebun test— the one context where that segfaults.Its sibling
amicode-connections.test.tsdoes this inbeforeEachand consequently passes:Adding the same to the routes test removes the segfault; the test then runs normally.
AMICO_PASQAL_KEYCHAIN_SERVICEis also absent from that file'sENV_KEYS, despitepasqal-secret.tsdocumenting it as existing so "an isolated sandbox / test run gets its own slot and never shares the real connection's secret."Revised actions
v1.17.3-amicode.10is not blocked by this. The remaining blocker for a greenunit (linux)is this test, not the product.amicode-connections-routes.test.ts(and addAMICO_PASQAL_KEYCHAIN_SERVICEtoENV_KEYSfor belt and braces). Fixes the crash and stops unit tests touching a developer's real keyring.try/catcharound the keychain write inkeychainSecretStore.write()assumes a JS throw. A native fault bypasses it entirely. Not reachable in production per the above, but the comment "a keychain that cannot be reached never throws into the caller" is stronger than whattry/catchcan actually guarantee around a NAPI call.Apologies for the churn on the release call — the earlier claim was made before I could get a working login onto the production path.
test(server): isolate the keychain in the connections-route suite