containerd/containerd · error

unsupported client type %T

Error message

unsupported client type %T

What it means

Returned by sandbox.NewClient (core/sandbox/bridge.go:37) when the passed client is neither a *ttrpc.Client nor a grpc.ClientConnInterface. It is a programming/configuration error: only TTRPC and gRPC sandbox clients are supported for bridging to the sandbox service API.

Source

Thrown at core/sandbox/bridge.go:37

// NewClient returns a new sandbox client that handles both GRPC and TTRPC clients.
func NewClient(client any) (api.TTRPCSandboxService, error) {
	switch c := client.(type) {
	case *ttrpc.Client:
		return api.NewTTRPCSandboxClient(c), nil
	case grpc.ClientConnInterface:
		return &grpcBridge{api.NewSandboxClient(c)}, nil
	default:
		return nil, fmt.Errorf("unsupported client type %T", client)
	}
}

type grpcBridge struct {
	client api.SandboxClient
}

var _ api.TTRPCSandboxService = (*grpcBridge)(nil)

func (g *grpcBridge) CreateSandbox(ctx context.Context, request *api.CreateSandboxRequest) (*api.CreateSandboxResponse, error) {
	return g.client.CreateSandbox(ctx, request)
}

func (g *grpcBridge) StartSandbox(ctx context.Context, request *api.StartSandboxRequest) (*api.StartSandboxResponse, error) {
	return g.client.StartSandbox(ctx, request)
}

View on GitHub (pinned to 4246446a2b)

Solutions

  1. Pass a *ttrpc.Client or a *grpc.ClientConn (any grpc.ClientConnInterface) obtained from the shim's connection.
  2. Convert your connection: use ttrpc.NewClient(conn) or grpc.NewClient(addr/sock) before calling NewClient.
  3. Check the caller (Create/Start/getSandbox) is forwarding the correct client type from the container's shim connection.

Example fix

// before
cl, err := sandbox.NewClient(ctx, rawNetConn)
// after
cl, err := sandbox.NewClient(ctx, ttrpc.NewClient(rawNetConn))
Defensive patterns

Strategy: type-guard

Validate before calling

switch c := client.(type) {
case *ttrpc.Client, grpc.ClientConnInterface:
    // ok
default:
    return fmt.Errorf("need ttrpc.Client or grpc ClientConnInterface, got %T", client)
}

Type guard

func isSandboxClient(c any) bool {
    switch c.(type) {
    case *ttrpc.Client:
        return true
    case grpc.ClientConnInterface:
        return true
    }
    return false
}

Try / catch

if err != nil {
    if strings.Contains(err.Error(), "unsupported client type") {
        return fmt.Errorf("bridge the connection via ttrpc.NewClient or grpc.NewClient first")
    }
    return err
}

Prevention

When it happens

Trigger: Calling sandbox.NewClient (directly or via sandbox Create/Start/getSandbox paths) with a client value that is neither *ttrpc.Client nor a gRPC ClientConnInterface.

Common situations: Passing a raw net.Conn, a custom connection wrapper, or a client type from a different transport library into the sandbox bridge; API changes where callers previously passed a different client type.

Related errors


AI-assisted analysis of containerd/containerd@4246446a2b (2026-09-02). Data as JSON: /api/errors/75b97e5380c0dfda. Report an issue: GitHub.