JuliusBrussee/caveman · error

native runtime current user SID: unavailable

Error message

native runtime current user SID: unavailable

What it means

On Windows, Serve builds an owner-only SDDL ACL from the current process token's user SID. If GetTokenUser succeeds but returns nil user or nil SID, the security descriptor cannot be constructed and the pipe cannot be safely restricted to one user, so Serve fails closed instead of opening an unsecured pipe.

Source

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

}

func dialNativeRuntime(ctx context.Context, home string) (net.Conn, error) {
	return winio.DialPipeContext(ctx, SocketPath(home))
}

// Serve exposes the same bounded JSON protocol over a user-only Windows named
// pipe. go-winio rejects remote clients at pipe creation; explicit owner SID
// ACL prevents another local user from attaching.
func Serve(ctx context.Context, home string, runtime *Runtime) error {
	if runtime == nil || runtime.store == nil {
		return errors.New("native runtime: store is required")
	}
	user, err := windows.GetCurrentProcessToken().GetTokenUser()
	if err != nil {
		return fmt.Errorf("native runtime current user SID: %w", err)
	}
	if user == nil || user.User.Sid == nil {
		return errors.New("native runtime current user SID: unavailable")
	}
	sddl := "D:P(A;;GA;;;" + user.User.Sid.String() + ")"
	listener, err := winio.ListenPipe(SocketPath(home), &winio.PipeConfig{
		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 {

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Run the runtime under a normal user account with a well-formed token (interactive user or standard service account)
  2. If running as a service, configure the service logon account so the process token carries a user SID
  3. Check for impersonation in the parent process that may have replaced the process token

Example fix

// before: launching via a wrapper that calls ImpersonateNamedPipeClient before Serve
// token has no owner SID -> error

// after: call Serve on a process whose own token carries the user SID
err := nativeruntime.Serve(ctx, home, rt) // run directly under the user account, not under impersonation
Defensive patterns

Strategy: fallback

Try / catch

if err := nativeruntime.Serve(ctx, home, rt); err != nil {
    if strings.Contains(err.Error(), "SID: unavailable") {
        // relaunch under a normal user account / unmodified service token
    }
}

Prevention

When it happens

Trigger: Running the runtime under an account whose token exposes no user SID — some service accounts, sandboxed contexts, or tokens mangled by privilege-dropping/impersonation; unusual Windows container or SSH-session token configurations.

Common situations: Running as SYSTEM via a custom service wrapper that strips token info; running inside restricted containers (Server Core variants, sandboxing products); CI agents with modified token privileges.

Related errors


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