{"record":{"id":"0a00a420fcc43b87","repo":"dapr/dapr","slug":"failed-to-build-http-client-for-q-w","errorCode":null,"errorMessage":"failed to build HTTP client for %q: %w","messagePattern":"failed to build HTTP client for %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/runtime/wfengine/inprocess/mcp/v1/session.go","lineNumber":149,"sourceCode":"\tif !h.closed.CompareAndSwap(false, true) {\n\t\treturn\n\t}\n\th.lifecycleCancel()\n\th.mu.Lock()\n\tdefer h.mu.Unlock()\n\tif s := h.session.Load(); s != nil {\n\t\t(*s).Close()\n\t\th.session.Store(nil)\n\t}\n}\n\n// connect builds an HTTP client, transport, and MCP session.\n// The caller's context controls the connection deadline.\n// The lifecycleCtx is passed separately for background work (token refresh) that must outlive the connect call.\nfunc (h *SessionHolder) connect(ctx context.Context) (*mcp.ClientSession, error) {\n\thttpClient, err := mcpauth.BuildHTTPClient(ctx, h.lifecycleCtx, h.server, h.store, h.sec)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to build HTTP client for %q: %w\", h.server.Name, err)\n\t}\n\n\ttransport, err := buildTransport(h.server, httpClient)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to build transport for %q: %w\", h.server.Name, err)\n\t}\n\n\tworkerLog.Debugf(\"connecting to MCP server %q\", h.server.Name)\n\tc := mcp.NewClient(&mcp.Implementation{Name: mcpClientName, Version: mcpClientVersion}, &mcp.ClientOptions{\n\t\tKeepAlive: keepAliveInterval,\n\t})\n\tsession, err := c.Connect(ctx, transport, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to connect to MCP server %q: %w\", h.server.Name, err)\n\t}\n\treturn session, nil\n}\n","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/dapr/dapr/blob/74ad41702745709bb15fe2114ff693b8c59bc3cc/pkg/runtime/wfengine/inprocess/mcp/v1/session.go#L131-L167","documentation":"Thrown by SessionHolder.connect when mcpauth.BuildHTTPClient fails while creating the HTTP client for the MCP server. BuildHTTPClient resolves the auth config on the MCPServer manifest: OAuth2 client credentials, OIDC discovery, mTLS/CA material from secret refs, and SPIFFE identity via the security handler. Since NewSessionHolder connects eagerly, this error surfaces at MCP-server registration time (analogous to component Init failure) and again on every reconnect attempt.","triggerScenarios":"MCPServer spec declares auth (e.g. endpoint.streamableHTTP with oauth2/static bearer via secretKeyRefs) and any of: the secret reference cannot be resolved from the configured secret store; the OIDC issuer/token endpoint is unreachable or returns invalid metadata; client credentials are rejected at discovery; caCert PEM is invalid; the security handler is required but unavailable.","commonSituations":"secretName/secretKey typo in the manifest; secret store component not loaded before the MCPServer; issuer URL blocked by network policy from the daprd pod; expired/rotated client secret; wrong tokenEndpoint URL; missing SPIFFE/SPIRE setup when workload identity is enabled.","solutions":["Check the wrapped error — BuildHTTPClient names the failing step (secret fetch, discovery, cert parse)","Verify every secretKeyRef in the MCPServer manifest exists in the declared secret store (kubectl get secret / dapr components)","From inside the daprd pod, curl the issuer/token endpoint to confirm reachability and valid TLS","Validate the caCert/clientCert PEM blocks decode (openssl x509 -noout -text)","Fix credentials if the wrapped error is an auth rejection from the identity provider","Temporarily strip auth from the manifest to confirm connectivity, then re-add auth piecewise"],"exampleFix":"# before: secret ref that does not exist\nspec:\n  endpoint:\n    streamableHTTP:\n      url: https://mcp.internal/tools\n      auth:\n        oauth2:\n          clientSecret:\n            secretKeyRef:\n              name: mcp-secrets\n              key: client-secret-typo\n\n# after: key matches the actual secret entry\n              key: client-secret","handlingStrategy":"retry","validationCode":"// Pre-flight the auth surface before registering the MCPServer\nfunc preflightAuth(ctx context.Context, srv *mcpserverapi.MCPServer, store *compstore.ComponentStore) error {\n    for _, ref := range collectSecretRefs(srv) {\n        if _, err := store.GetSecret(ctx, ref); err != nil {\n            return fmt.Errorf(\"secret %q unresolvable: %w\", ref, err)\n        }\n    }\n    return nil\n}","typeGuard":"func isHTTPClientBuildFailure(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"failed to build HTTP client\")\n}","tryCatchPattern":"holder, err := NewSessionHolder(ctx, srv, store, sec)\nif err != nil && isHTTPClientBuildFailure(err) {\n    // config errors are permanent; network blips during OIDC discovery are not —\n    // retry a bounded number of times before surfacing\n    for range 3 {\n        time.Sleep(2 * time.Second)\n        if holder, err = NewSessionHolder(ctx, srv, store, sec); err == nil { break }\n        if !isHTTPClientBuildFailure(err) { break }\n    }\n}\nif err != nil { return err }","preventionTips":["Dry-run secret refs against the real secret store before applying the manifest","Keep the issuer/token endpoint reachable from the daprd pod (network policy, DNS, TLS CA)","Rotate OAuth credentials with overlap and update the secret before restarting daprd","Validate caCert PEM files in CI with openssl"],"tags":["auth","oauth2","network","secrets","mcp"],"backgroundTag":null,"analyzedSha":"74ad41702745709bb15fe2114ff693b8c59bc3cc","analyzedAt":"2026-08-16T04:22:26.543Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}