microsoft/typescript-go · error

failed to create pipe transport

Error message

failed to create pipe transport: %w

What it means

StdioServer.Run failed to create the pipe transport: NewPipeTransport could not listen on the configured PipePath (a Unix domain socket on Unix, a named pipe on Windows). The underlying net.Listen error is wrapped, so the message includes the OS-level cause (address already in use, permission denied, socket path too long, invalid pipe name). The server cannot start and Run returns before accepting any connection.

Solutions

  1. Remove the stale socket file at PipePath before starting (os.Remove(path), ignoring not-exist), then retry Run
  2. Use a unique PipePath per server instance (e.g. include PID or generate a temp name) to avoid collisions
  3. Shorten the path so it fits the OS limit, or place sockets in a short directory like /tmp
  4. Check the wrapped error text for the OS cause: 'address already in use' -> stale socket, 'permission denied' -> directory rights, 'invalid argument' -> bad pipe name/format

Example fix

// before
srv := api.NewStdioServer(&api.StdioServerOptions{Cwd: cwd, PipePath: "/tmp/ts-api.sock"})
err := srv.Run(ctx) // failed to create pipe transport: address already in use

// after
const sock = "/tmp/ts-api.sock"
if _, err := os.Stat(sock); err == nil {
    _ = os.Remove(sock) // clear stale socket from a crashed run
}
srv := api.NewStdioServer(&api.StdioServerOptions{Cwd: cwd, PipePath: sock})
err := srv.Run(ctx)
Defensive patterns

Strategy: validation

Validate before calling

// Go: clear a stale socket and sanity-check the path before Run.
func preparePipePath(path string) error {
    if fi, err := os.Stat(path); err == nil {
        if fi.Mode()&os.ModeSocket != 0 {
            if err := os.Remove(path); err != nil {
                return fmt.Errorf("stale socket %s not removable: %w", path, err)
            }
        } else {
            return fmt.Errorf("%s exists and is not a socket", path)
        }
    }
    if runtime.GOOS != "windows" && len(path) >= 104 {
        return fmt.Errorf("socket path too long for AF_UNIX: %s", path)
    }
    return nil
}

Try / catch

err := srv.Run(ctx)
if err != nil && strings.Contains(err.Error(), "failed to create pipe transport") {
    cause := errors.Unwrap(err)
    if errors.Is(cause, syscall.EADDRINUSE) {
        // stale socket: remove and restart once
        _ = os.Remove(pipePath)
        err = srv.Run(ctx)
    }
}

Prevention

When it happens

Trigger: A stale socket file already exists at PipePath from a previous crashed process (EADDRINUSE); the socket path exceeds the ~108-byte Unix domain socket limit; permission denied on the directory; two server instances configured with the same PipePath; a malformed Windows pipe name that is not under \\.\pipe\.

Common situations: Restarting a tool whose previous process did not clean up its socket; long temp-dir paths (CI runners, macOS $TMPDIR) blowing past the unix socket path limit; running two editor integrations side by side that both hardcode the same pipe path; sandboxed environments denying unix socket creation.

Related errors


AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16). Data as JSON: /api/errors/ad2680f707c676a2. Report an issue: GitHub.

Appendix: source

Thrown at internal/api/server.go:62

// NewStdioServer creates a new STDIO-based API server.
func NewStdioServer(options *StdioServerOptions) *StdioServer {
	if options.Cwd == "" {
		panic("StdioServerOptions.Cwd is required")
	}

	return &StdioServer{
		options: options,
	}
}

// Run starts the server and blocks until the connection closes.
func (s *StdioServer) Run(ctx context.Context) error {
	var transport Transport
	if s.options.PipePath != "" {
		t, err := NewPipeTransport(s.options.PipePath)
		if err != nil {
			return fmt.Errorf("failed to create pipe transport: %w", err)
		}
		defer t.Close()
		transport = t
	} else {
		t := NewStdioTransport(s.options.In, s.options.Out)
		defer t.Close()
		transport = t
	}

	fs := bundled.WrapFS(osvfs.FS())

	// Wrap the base FS with callbackFS if callbacks are requested
	var callbackFS *callbackFS
	if len(s.options.Callbacks) > 0 {
		callbackFS = newCallbackFS(fs, s.options.Callbacks)
		fs = callbackFS
	}

View on GitHub (pinned to 1bcfa18d79)