JuliusBrussee/caveman · error

native runtime named-pipe accept: %w

Error message

native runtime named-pipe accept: %w

What it means

Returned when listener.Accept() fails on the Windows named pipe and neither the context is cancelled nor the error is net.ErrClosed. This is an unexpected accept failure on the pipe listener — typically a broken pipe instance or OS-level error, since intentional shutdown is filtered by the ctx.Err()/ErrClosed checks.

Source

Thrown at proxy/internal/nativeruntime/server_windows.go:68

		SecurityDescriptor: sddl,
		InputBufferSize:    maxRequestBytes,
		OutputBufferSize:   maxRequestBytes,
	})
	if err != nil {
		return fmt.Errorf("native runtime named-pipe listen: %w", err)
	}
	defer listener.Close()
	go func() {
		<-ctx.Done()
		_ = listener.Close()
	}()
	for {
		conn, err := listener.Accept()
		if err != nil {
			if ctx.Err() != nil || errors.Is(err, net.ErrClosed) {
				return nil
			}
			return fmt.Errorf("native runtime named-pipe accept: %w", err)
		}
		go serveConn(ctx, conn, runtime)
	}
}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Update go-winio to the latest version — accept/retry logic around broken pipe instances has seen fixes
  2. Exclude the caveman pipe from AV/EDR scanning and prevent external tools from closing its handles
  3. Add a resilience wrapper: on error, close the listener and re-enter Serve while ctx is alive
  4. Check Windows Event Log for pipe/FS errors if it recurs

Example fix

// before: any non-shutdown accept error terminates Serve
return fmt.Errorf("native runtime named-pipe accept: %w", err)

// after: supervise the server loop so a transient accept failure restarts the pipe
for {
    if err := nativeruntime.Serve(ctx, home, rt); err != nil && ctx.Err() == nil {
        log.Warn("native runtime exited, restarting", "error", err)
        time.Sleep(time.Second)
        continue
    }
    return err
}
Defensive patterns

Strategy: retry

Validate before calling

null

Try / catch

for {
    err := nativeruntime.Serve(ctx, home, rt)
    if err == nil || ctx.Err() != nil { return err }
    var ne net.Error
    if errors.As(err, &ne) || strings.Contains(err.Error(), "named-pipe accept") {
        log.Warn("pipe accept failed; recreating listener", "error", err)
        select { case <-time.After(time.Second): case <-ctx.Done(): return ctx.Err() }
        continue
    }
    return err
}

Prevention

When it happens

Trigger: Long-running Serve on Windows where the underlying named pipe instance is destroyed by an external handle close, or a Win32 accept error (pipe disconnected in an unrecoverable state) occurs while ctx is still live.

Common situations: Tooling (Process Explorer, debuggers) or AV software closing pipe handles; a go-winio version mismatch with the installed Windows version; the process running under a context that loses the pipe namespace after a session change (fast user switching, RDP disconnect).

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/7611c89d436bd3ac. Report an issue: GitHub.