containerd/containerd · error

errdefs.ErrNotImplemented

errdefs.ErrNotImplemented

Error message

unsupported events client %T: %w

What it means

NewRemoteEvents accepts a GRPC or TTRPC client; any other client type hits the default case and panics with an ErrNotImplemented error. This is a programming/typing mistake: the library only supports *grpc.ClientConn and *ttrpc.Client for remote event proxying.

Source

Thrown at core/events/proxy/remote_events.go:61

	switch c := client.(type) {
	case api.EventsClient:
		return &grpcEventsProxy{
			client: c,
		}
	case api.TTRPCEventsClient:
		return &ttrpcEventsProxy{
			client: c,
		}
	case grpc.ClientConnInterface:
		return &grpcEventsProxy{
			client: api.NewEventsClient(c),
		}
	case *ttrpc.Client:
		return &ttrpcEventsProxy{
			client: api.NewTTRPCEventsClient(c),
		}
	default:
		panic(fmt.Errorf("unsupported events client %T: %w", client, errdefs.ErrNotImplemented))
	}
}

type grpcEventsProxy struct {
	client api.EventsClient
}

func (p *grpcEventsProxy) Publish(ctx context.Context, topic string, event events.Event) error {
	evt, err := typeurl.MarshalAny(event)
	if err != nil {
		return err
	}
	req := &api.PublishRequest{
		Topic: topic,
		Event: typeurl.MarshalProto(evt),
	}
	if _, err := p.client.Publish(ctx, req); err != nil {
		return errgrpc.ToNative(err)

View on GitHub (pinned to 4246446a2b)

Solutions

  1. Pass a *grpc.ClientConn or *ttrpc.Client as the client argument
  2. Type-switch or assert the concrete client type before calling NewRemoteEvents
  3. Obtain the correct client from the containerd client instance (e.g. client.Conn() for grpc)

Example fix

// before
err := remote.NewRemoteEvents(ctx, myWrappedClient, ns) // panics
// after
conn, _ := myWrappedClient.GRPCConn()
err := remote.NewRemoteEvents(ctx, conn, ns)
Defensive patterns

Strategy: type-guard

Validate before calling

switch c := client.(type) {
case *grpc.ClientConn, *ttrpc.Client:
    // ok
default:
    return fmt.Errorf("unsupported events client %T", client)
}

Type guard

func isSupportedEventsClient(client any) bool {
    switch client.(type) {
    case *grpc.ClientConn, *ttrpc.Client:
        return true
    }
    return false
}

Try / catch

func() (err error) {
    defer func() { if r := recover(); r != nil { err = fmt.Errorf("NewRemoteEvents: %v", r) } }()
    return remote.NewRemoteEvents(ctx, client, ns)
}

Prevention

When it happens

Trigger: Calling events.NewRemoteEvents(ctx, client, ...) with a client other than *grpc.ClientConn or *ttrpc.Client — e.g. passing a wrapped client, an interface value, or nil of the wrong concrete type.

Common situations: Passing a custom dialer's connection wrapper; mixing containerd client internals between ttrpc and grpc; a refactor that changed the client type without updating the call.

Related errors


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