grpc/grpc-go · critical

Manual resolver instance has not yet been built.

Error message

Manual resolver instance has not yet been built.

What it means

The manual resolver (resolver/manual/manual.go:123) exposes CC() to get the ClientConn it was wired into. CC() panics if the resolver has not been Build() yet (r.cc == nil), because there is no connection to return. The manual resolver is primarily a testing/dialing helper; this panic signals the resolver was used before dialing.

Source

Thrown at resolver/manual/manual.go:127

// InitialState.
func (r *Resolver) UpdateState(s resolver.State) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.lastSeenState = &s
	if r.cc == nil {
		return
	}
	err := r.cc.UpdateState(s)
	r.UpdateStateCallback(err)
}

// CC returns r's ClientConn when r was last Built.  Panics if the resolver has
// not been Built before.
func (r *Resolver) CC() resolver.ClientConn {
	r.mu.Lock()
	defer r.mu.Unlock()
	if r.cc == nil {
		panic("Manual resolver instance has not yet been built.")
	}
	return r.cc
}

View on GitHub (pinned to 03255a9237)

Solutions

  1. Ensure grpc.Dial/ NewClient with the manual scheme has been called (and Build invoked) before calling CC().
  2. Use UpdateState() instead of CC() for seeding initial state - UpdateState is safe before Build (it stores lastSeenState).
  3. If you need the ClientConn, capture the one returned by grpc.Dial/ NewClient directly rather than via r.CC().

Example fix

// before
r, _ := manual.GenerateAndRegisterManualResolver()
cc := r.CC() // panics: not built yet

// after
r, _ := manual.GenerateAndRegisterManualResolver()
conn, _ := grpc.NewClient(r.Scheme()+":///unused", grpc.WithResolvers(r))
// now r.CC() is safe, or just use conn directly
Defensive patterns

Strategy: validation

Validate before calling

// Dial before using CC(); use UpdateState for pre-build state
r, _ := manual.GenerateAndRegisterManualResolver()
conn, _ := grpc.NewClient(r.Scheme()+":///unused", grpc.WithResolvers(r))
// Now r.CC() is safe; or simply use conn.
// For initial state before build, prefer:
// r.UpdateState(resolver.State{...}) // safe pre-build

Prevention

When it happens

Trigger: Calling r.CC() on a manual.Resolver before grpc.Dial has invoked the resolver's Build (i.e., before the channel actually started using it). Also calling CC() after Close() if cc was cleared.

Common situations: Test code grabbing r.CC() to push UpdateState before the dial completed; reordering setup so CC() is called before Dial; using a manual resolver as if it were already connected.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/aa452786715a8278. Report an issue: GitHub.