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
- Ensure grpc.Dial/ NewClient with the manual scheme has been called (and Build invoked) before calling CC().
- Use UpdateState() instead of CC() for seeding initial state - UpdateState is safe before Build (it stores lastSeenState).
- 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
- Always call grpc.Dial/ NewClient before invoking CC() on a manual resolver.
- Use UpdateState() (not CC()) to seed initial resolver state - it is safe before Build.
- Capture the ClientConn returned by Dial directly rather than round-tripping through r.CC().
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
- bad resolver state
- no children to pick from
- metadata: Pairs got the odd number of input pairs for metada
- metadata: AppendToOutgoingContext got an odd number of input
- metadata: FromOutgoingContext got an odd number of input pai
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/aa452786715a8278.
Report an issue: GitHub.