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
Native-compiled skelpo-shop-admin (./build/server) at perry 0.5.914 crashes with SIGBUS / exit 138 during POST /v1/auth/signup. The first INSERT INTO users commits (visible in MySQL), then the process dies — curl gets Empty reply from server and the connection drops. tsx server/main.ts against the same source + same DB returns the correct JSON / HTTP 201.
This is the v0.5.914 surfacing of what at v0.5.912 was a silent body-0/HTTP 200 — same site, harder failure now that the Invalid-Date sentinel is in.
U1 INSERT ← createUser INSERT runs
U2 done, pre getUserById ← INSERT resolved, row visible
G1 pre queryOne ← getUserById entered
G2 queryOne obj ← SELECT returned the row
T0 enter (toUser entered)
T1 id=019e2fac-... ← bufToId(r.id) works
T2 emailVerified=null ← dtToIso(null) works
T3 totp=null ← dtToIso(null) works
← SIGBUS. No T4.
Next statement is dtRequiredToIso(r.createdAt), which calls r.createdAt.toDate().getTime() on the MyDateTime object the @perryts/mysql driver decoded. That .toDate() call is where the process dies.
Root cause
The proximate trigger is #788 — closure-captured numeric params read as 0 inside object-literal : Date method (filed separately). MyDateTime.toDate() is exactly that shape, so for every decoded DATETIME the captured y/mo/d/... read as 0 inside the method and new Date(Date.UTC(0, ...)) produces a year-0 Date (-62155492224598 ms). When that value flows into dtRequiredToIso → d.toISOString() and the resulting string into perry's JSON / fastify reply path, something in the formatting / serialization chain assumes a finite, plausible timestamp and segfaults.
In a standalone repro of just the MyDateTime.toDate().toISOString() shape (no Fastify, no mysql connection — see #788) the same code returns a garbage 0000-05-15T17:29:36.598Z string without crashing. The shop-admin path additionally goes through fastify's JSON reply, the post-#748wait_for_promise condvar path, and likely the @perryts/mysql worker-thread → main-thread Date hand-off. One of those layers can't survive a year-0 / Invalid Date and faults.
Repro
Local: clean checkout of skelpo-shop-admin, cargo build --release of perry at HEAD (e18cdb07 / v0.5.914), then:
mysql -uroot -e "DROP DATABASE IF EXISTS shopadmin; CREATE DATABASE shopadmin"
KEK_BASE64=... JWT_SIGNING_KEY_PEM=... DB_DSN='mysql://root:@127.0.0.1:3306/shopadmin' \
BIND_ADDR='127.0.0.1:18080' ./build/server &
curl -X POST http://127.0.0.1:18080/v1/auth/signup \
-H 'content-type: application/json' \
-d '{"email":"x@y.com","password":"correct-horse-battery-staple","accountName":"X"}'
# → curl: (52) Empty reply from server; server process exits 138.
tsx server/main.ts against the same DB+env returns the correct {accessToken, refreshToken, expiresAt, user, account} payload with HTTP 201 and all five tables populated.
Tracking history
This is the same end-user failure that drove #665 and #748:
Independently, find the downstream layer that segfaults on a year-0/Invalid Date and harden it — same value at the same call site shouldn't take down the process. (My standalone repro of the same shape returns a garbage string without crashing, so the segfault is specific to one of: fastify reply path, @perryts/mysql worker→main thread Date crossing, or wait_for_promise resuming with a Date argument it doesn't expect.)
Symptom
Native-compiled skelpo-shop-admin (
./build/server) at perry 0.5.914 crashes with SIGBUS / exit 138 duringPOST /v1/auth/signup. The firstINSERT INTO userscommits (visible in MySQL), then the process dies —curlgetsEmpty reply from serverand the connection drops.tsx server/main.tsagainst the same source + same DB returns the correct JSON / HTTP 201.This is the v0.5.914 surfacing of what at v0.5.912 was a silent body-
0/HTTP 200 — same site, harder failure now that the Invalid-Date sentinel is in.Pinned crash site (checkpoint-instrumented trace)
Instrumented
server/persistence/users.ts::createUser/getUserById/toUser:Next statement is
dtRequiredToIso(r.createdAt), which callsr.createdAt.toDate().getTime()on theMyDateTimeobject the@perryts/mysqldriver decoded. That.toDate()call is where the process dies.Root cause
The proximate trigger is #788 — closure-captured numeric params read as 0 inside object-literal
: Datemethod (filed separately).MyDateTime.toDate()is exactly that shape, so for every decoded DATETIME the capturedy/mo/d/...read as 0 inside the method andnew Date(Date.UTC(0, ...))produces a year-0 Date (-62155492224598ms). When that value flows intodtRequiredToIso → d.toISOString()and the resulting string into perry's JSON / fastify reply path, something in the formatting / serialization chain assumes a finite, plausible timestamp and segfaults.In a standalone repro of just the
MyDateTime.toDate().toISOString()shape (no Fastify, no mysql connection — see #788) the same code returns a garbage0000-05-15T17:29:36.598Zstring without crashing. The shop-admin path additionally goes through fastify's JSON reply, the post-#748wait_for_promisecondvar path, and likely the @perryts/mysql worker-thread → main-thread Date hand-off. One of those layers can't survive a year-0 / Invalid Date and faults.Repro
Local: clean checkout of skelpo-shop-admin,
cargo build --releaseof perry at HEAD (e18cdb07/ v0.5.914), then:tsx server/main.tsagainst the same DB+env returns the correct{accessToken, refreshToken, expiresAt, user, account}payload with HTTP 201 and all five tables populated.Tracking history
This is the same end-user failure that drove #665 and #748:
0+ HTTP 200 after sequence of @perryts/mysql writes; first INSERT commits, rest silently no-op, explicit return dropped #748 —wait_for_promise1s polling cap (closed at v0.5.912), then Invalid-Date sentinel (closed at v0.5.914 bydc6be087). Both landed correctly.: Dateobject-method) once that's fixed. Fixing AsyncLocalStorage: real async-context tracking across await / microtasks / timers #788 should make this go away; filing separately so the segfault stays visible in the meantime in case the downstream formatter/reply path needs a defensive fix even after AsyncLocalStorage: real async-context tracking across await / microtasks / timers #788 lands.Suggested fix scope
toISOStringreturns the real timestamp.wait_for_promiseresuming with a Date argument it doesn't expect.)Perry version at repro: 0.5.914.