{"record":{"id":"bbea590dc10effd7","repo":"amir20/dozzle","slug":"cloud-dial-w","errorCode":null,"errorMessage":"cloud: dial: %w","messagePattern":"cloud: dial: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cloud/search.go","lineNumber":65,"sourceCode":"\n// unaryServiceClient returns a (lazily dialed) reusable gRPC client. The\n// underlying conn is shared across every Dozzle-initiated unary call so we pay\n// the TLS handshake once per process — not once per keystroke or scroll.\nfunc (c *Client) unaryServiceClient() (pb.CloudToolServiceClient, error) {\n\tc.unaryConnMu.Lock()\n\tdefer c.unaryConnMu.Unlock()\n\tif c.unaryClient != nil {\n\t\treturn c.unaryClient, nil\n\t}\n\tvar creds grpc.DialOption\n\tif c.plaintext {\n\t\tcreds = grpc.WithTransportCredentials(insecure.NewCredentials())\n\t} else {\n\t\tcreds = grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, \"\"))\n\t}\n\tconn, err := grpc.NewClient(c.target, creds)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cloud: dial: %w\", err)\n\t}\n\tc.unaryConn = conn\n\tc.unaryClient = pb.NewCloudToolServiceClient(conn)\n\treturn c.unaryClient, nil\n}\n\n// SearchLogs runs a Cloud-side log search against the existing gRPC service.\n// Reuses a long-lived gRPC conn (lazily dialed on first call) so the\n// 500ms search timeout isn't burned on a TLS handshake per keystroke.\n// Identity (user, instance) is enforced server-side from the authenticated\n// metadata; this client passes only the per-request fields below.\nfunc (c *Client) SearchLogs(ctx context.Context, query string, limit int32, hostID, containerID string, before int64) (*SearchLogResult, error) {\n\tapiKey := c.apiKeyFunc()\n\tif apiKey == \"\" {\n\t\treturn nil, ErrNotConfigured\n\t}\n\n\tclient, err := c.unaryServiceClient()","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/amir20/dozzle/blob/d9463cbe21874e44ab79db6fa63e746ca7d22928/internal/cloud/search.go#L47-L83","documentation":"unaryServiceClient creates the lazily-initialized gRPC client connection to the cloud service with grpc.NewClient and wraps any dial/client-construction failure as 'cloud: dial: %w'. Without this connection, both GetAlerts and SearchLogs cannot run. TLS credentials are chosen based on whether the target is insecure.","triggerScenarios":"grpc.NewClient returns an error for the configured target: malformed target/address string, invalid target scheme, or credentials setup failure when creating the connection.","commonSituations":"Misconfigured cloud endpoint URL (bad scheme or host), environment variable pointing at a wrong address, target string with stray whitespace or unsupported prefix.","solutions":["Check the cloud target/address configuration for typos and unsupported schemes","Confirm the target format is valid for grpc.NewClient (e.g. dns:///host:port or host:port)","Verify TLS expectations: if the endpoint is plaintext, the insecure path must be selected correctly","Test endpoint reachability with a simple gRPC client or grpcurl before app startup"],"exampleFix":"// before\ntarget := os.Getenv(\"DOZZLE_CLOUD_URL\") // e.g. \"https://cloud.example.com\"\n// after: strip scheme and use grpc-friendly target\ntarget := strings.TrimPrefix(os.Getenv(\"DOZZLE_CLOUD_URL\"), \"https://\")\nconn, err := grpc.NewClient(target, creds)","handlingStrategy":"validation","validationCode":"target := strings.TrimSpace(cfg.CloudTarget)\nif target == \"\" || strings.Contains(target, \" \") || !strings.Contains(target, \":\") {\n    return fmt.Errorf(\"invalid cloud target: %q\", target)\n}","typeGuard":null,"tryCatchPattern":"cl, err := unaryServiceClient(ctx)\nif err != nil {\n    return nil, fmt.Errorf(\"cloud unavailable (%v), check DOZZLE_CLOUD target config\", err)\n}","preventionTips":["Validate the cloud target address format at startup","Use grpcurl against the endpoint during deployment checks","Strip http(s):// schemes before passing to grpc.NewClient","Centralize target construction in one config function"],"tags":["grpc","cloud","network","dial","connection"],"backgroundTag":"connection-refused","analyzedSha":"d9463cbe21874e44ab79db6fa63e746ca7d22928","analyzedAt":"2026-09-07T10:08:55.855Z","contentChangedAt":"2026-09-07T10:08:55.855Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}