{"record":{"id":"0c60fb4223f018f1","repo":"grpc/grpc-go","slug":"failed-to-exit-idle-mode-w","errorCode":null,"errorMessage":"failed to exit idle mode: %w","messagePattern":"failed to exit idle mode: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"clientconn.go","lineNumber":303,"sourceCode":"\t// resolution on the client.\n\topts = append([]DialOption{withDefaultScheme(\"passthrough\"), WithLocalDNSResolution()}, opts...)\n\tcc, err := NewClient(target, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// We start the channel off in idle mode, but kick it out of idle now,\n\t// instead of waiting for the first RPC.  This is the legacy behavior of\n\t// Dial.\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tcc.Close()\n\t\t}\n\t}()\n\n\t// This creates the name resolver, load balancer, etc.\n\tif err := cc.exitIdleMode(); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to exit idle mode: %w\", err)\n\t}\n\tcc.idlenessMgr.UnsafeSetNotIdle()\n\n\t// Return now for non-blocking dials.\n\tif !cc.dopts.block {\n\t\treturn cc, nil\n\t}\n\n\tif cc.dopts.timeout > 0 {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(ctx, cc.dopts.timeout)\n\t\tdefer cancel()\n\t}\n\tdefer func() {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tswitch {\n\t\t\tcase ctx.Err() == err:","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/clientconn.go#L285-L321","documentation":"DialContext (deprecated, legacy) calls exitIdleMode() to eagerly start the resolver and load balancer (clientconn.go:302-304). If that fails (most commonly the resolver cannot start), DialContext wraps the underlying error and returns nil for the connection. The wrapped error usually comes from resolverWrapper.start().","triggerScenarios":"grpc.Dial or grpc.DialContext is used and exitIdleMode fails at clientconn.go:407 (resolverWrapper.start returns an error), which is then wrapped at clientconn.go:303.","commonSituations":"The dial target uses a scheme whose resolver builder is not registered; the resolver build itself errors (e.g., malformed URL, DNS resolver unavailable); a custom resolver returning an error from Build.","solutions":["Inspect the wrapped error (the %w chain) to find the root cause from the resolver.","Verify the target scheme is registered (e.g., import the resolver package, or use a scheme like \"dns:///\" or \"passthrough:///\").","Prefer grpc.NewClient over Dial/DialContext; NewClient starts in idle and reports resolver errors lazily, avoiding this eager-failure path."],"exampleFix":"// before\ncc, err := grpc.DialContext(ctx, \"myscheme:///host\", grpc.WithBlock())\n// after\ncc, err := grpc.NewClient(\"dns:///host\", grpc.WithTransportCredentials(insecure.NewCredentials()))\nif err != nil { log.Fatal(err) }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"cc, err := grpc.DialContext(ctx, target, opts...)\nif err != nil {\n    if errors.Is(err, errConnClosing) { /* channel closed during dial */ }\n    // unwrap to inspect the resolver error\n    return fmt.Errorf(\"dial failed: %w\", err)\n}","preventionTips":["Prefer grpc.NewClient (non-blocking) over Dial/DialContext to avoid eager exitIdleMode failures.","Register the resolver for your scheme before dialing.","Always check and handle the error returned from Dial/DialContext."],"tags":["go","grpc","dial","resolver","idle"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}