Skip to content

fix(server): Windows socket read/write fails with GetLastError(87)#21

Open
TheRefreshCNFT wants to merge 5 commits intonullclaw:mainfrom
TheRefreshCNFT:fix/windows-socket-read
Open

fix(server): Windows socket read/write fails with GetLastError(87)#21
TheRefreshCNFT wants to merge 5 commits intonullclaw:mainfrom
TheRefreshCNFT:fix/windows-socket-read

Conversation

@TheRefreshCNFT
Copy link

@TheRefreshCNFT TheRefreshCNFT commented Mar 25, 2026

Windows compatibility fixes

Five fixes that get NullHub running correctly on Windows 10/11 with Zig 0.15.2.


1. Server socket I/O — ws2_32 recv/send (server.zig, net_compat.zig)

Problem: NullHub's HTTP server crashes immediately on Windows:
error.Unexpected: GetLastError(87): The parameter is incorrect.
std.os.windows.zig:648:53 in ReadFile
std.net.zig:2325:36 in read
server.zig:339:35 in handleConnection

Zig 0.15.2's std.net.Stream.read/write uses NtReadFile/NtWriteFile internally, which fail on sockets — Windows requires ws2_32.recv/ws2_32.send for socket I/O.

Fix: Adds net_compat.zig with a thin platform-aware compat layer (streamRead, streamWrite, streamWriteAll). All socket paths in server.zig go through it. No-op on non-Windows. Same class of fix as nullclaw/nullclaw#550.


2. Health check socket I/O — same compat layer (supervisor/health.zig)

Problem: The supervisor health probe has the same NtReadFile issue — stream.read/writeAll in health.zig also crash on Windows sockets.

Fix: Health check now uses net_compat.streamRead and net_compat.streamWriteAll, consistent with the server fix.


3. Binary path missing .exe on Windows (core/paths.zig)

Problem: Paths.binary() constructs paths like ~/.nullhub/bin/nullclaw-standalone — without the .exe suffix, the binary is never found on Windows and the instance fails to start.

Fix: Appends .exe on builtin.os.tag == .windows, no change on other platforms.


4. channel launch mode expands to channel start (core/launch_args.zig)

Problem: When an instance is configured with launch_mode: "channel", Hub passes nullclaw channel to the process — which prints usage and exits. The runnable command is nullclaw channel start.

Fix: buildLaunchArgs detects a bare channel token and appends start, making the mode actually supervisable. Includes test coverage.


5. Skip HTTP health checks for non-server launch modes (api/instances.zig)

Problem: Only agent mode was exempted from HTTP health probes. Modes like gateway and channel expose WebSocket/gateway traffic — not an HTTP health endpoint — so health probes would always fail, causing the supervisor to repeatedly mark instances as unhealthy.

Fix: Flips the logic: only serve mode gets HTTP health checks. All other modes (agent, gateway, channel, or any future non-HTTP mode) are supervised by process-alive checks only (port=0). No component names hardcoded.


Testing

Built and tested on Windows 10 x64, Zig 0.15.2:

  • /health{"status":"ok"}
  • /api/instances → valid JSON
  • Managed nullclaw instance starts and stays running under Hub supervision
  • No GetLastError(87) crashes

Related: nullclaw/nullclaw#317, nullclaw/nullclaw#550

…WriteFile

Zig 0.15.2's std.net.Stream.read/write uses ReadFile (via NtReadFile)
on Windows, which fails on sockets with GetLastError(87) 'The parameter
is incorrect'. This is the same class of bug fixed in nullclaw PR #550.

Adds net_compat.zig with Windows-safe socket I/O that uses ws2_32.recv
and ws2_32.send directly, falling back to std stream operations on other
platforms. All socket read/write paths in server.zig now go through the
compat layer.
On Windows, resolving nullhub.localhost blocks until the 350ms timeout
fires, causing a blank screen on load. The UI only renders after DevTools
is opened (which adds enough latency to let the timeout complete).

Fix: bail out early if already on the direct fallback host (127.0.0.1),
skipping the DNS probe entirely. The redirect is only needed when
arriving via a hostname alias.
CSS @import url() is render-blocking — the browser won't paint anything
until the external stylesheet finishes loading. On slow connections or
when Google Fonts stalls, this causes a black screen that only resolves
when DevTools is opened (triggering a repaint).

Move font loading to async <link> tags in app.html using the
media=print/onload=all pattern, with a noscript fallback.
… expansion

- paths.zig: append .exe suffix on Windows for binary path resolution
- supervisor/health.zig: use ws2_32 compat layer (net_compat.zig) for
  health check socket I/O, same fix as server.zig (NtReadFile fails on sockets)
- launch_args.zig: expand bare 'channel' launch_mode to 'channel start'
  so Hub can supervise nullclaw channel mode; adds test coverage
Only 'serve' mode exposes an HTTP health endpoint. All other modes
(agent, gateway, channel, or any future long-lived non-HTTP mode)
should be supervised by process-alive checks only (port=0).

Previously only 'agent' was exempted, meaning gateway/channel modes
would incorrectly attempt HTTP health probes and fail.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant