js/bun/util/filesystem_router.test.ts aborts. Pinned Bun scores 22 pass / 0 fail on the same file, so this is a Home gap.
thread N panic: reached unreachable code
string/wtf.zig:92 deref bun.assert(self.hasAtLeastOneRef())
api/filesystem_router.zig MatchedRoute.deinit -> origin.deref()
api/filesystem_router.zig finalize
.generated/ZigGeneratedClasses.zig MatchedRouteClass__finalize
runtime/timer/WTFTimer.zig fire / runWithoutRemovingThe assert is hasAtLeastOneRef(), i.e. the RefString is already at zero when MatchedRoute.deinit drops its reference. It runs from the GC finalizer, after origin should be validated and the skipped handles filenames containing byte 0xFF.
Ruled out
- Repeated deinit of one object.
445b0d5f3's follow-up made bothFileSystemRouter.finalizeandMatchedRoute.deinitclear each field before dropping it (mirroring the.take()in Bun'sfilesystem_router.rs:705-713and:851-859). The abort is unchanged, same line, same assert. - A second construction path.
grep 'MatchedRoute{'finds exactly one site, insideMatchedRoute.init, and that init refs all three strings (base_dir.ref(),o.ref(),prefix.ref()) exactly as Bun's does.
So the ref/deref accounting within filesystem_router.zig is symmetric.
Where I would look next
FileSystemRouter.fromJS builds the string at filesystem_router.zig:163:
.origin = if (origin_str.len > 0) vm.refCountedString(origin_str.slice(), null, true) else null,Bun's equivalent is vm.ref_counted_string::<true>(origin_str.slice(), None) (filesystem_router.rs:332-334). The question is whether Home's refCountedString returns an owned reference (count 1, caller must deref) or a borrowed/interned one whose count is managed by the VM's string cache — and whether that matches what both finalize paths assume. If it hands back a deduped entry the VM also drops, the count reaches zero before the finalizer runs, which is exactly the symptom.
Worth checking the same contract for asset_prefix and base_dir, which are acquired the same way.
Refs #66