{"record":{"id":"7cf2d053d8733d7a","repo":"dapr/dapr","slug":"failed-to-build-transport-for-q-w","errorCode":null,"errorMessage":"failed to build transport for %q: %w","messagePattern":"failed to build transport for %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/runtime/wfengine/inprocess/mcp/v1/session.go","lineNumber":154,"sourceCode":"\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\n// isConnectionClosed returns true if the error wraps mcp.ErrConnectionClosed.\nfunc isConnectionClosed(err error) bool {\n\treturn errors.Is(err, mcp.ErrConnectionClosed)\n}\n","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/dapr/dapr/blob/74ad41702745709bb15fe2114ff693b8c59bc3cc/pkg/runtime/wfengine/inprocess/mcp/v1/session.go#L136-L172","documentation":"Thrown by SessionHolder.connect when buildTransport cannot construct an MCP transport for the server. Looking at buildTransport (worker.go:277-314), the only failure path is the default branch: the MCPServer spec sets none of endpoint.streamableHTTP, endpoint.sse, or endpoint.stdio — the wrapped error reads 'no transport configured for MCPServer %q: set one of streamableHTTP, sse, or stdio'. It fires at eager connect, i.e. registration time.","triggerScenarios":"Applying an MCPServer manifest whose spec.endpoint omits all three transport blocks (or contains only unrelated fields), then Dapr attempts to create the session holder and register workflows.","commonSituations":"Manifest authored with a typo'd or misplaced endpoint field (e.g. url at the wrong level so the block is empty after unmarshal); CRD schema drift between dapr versions renaming endpoint fields; manifests generated by tooling that drops empty blocks; kubectl apply pruning fields unknown to the stored CRD version.","solutions":["Read the wrapped message — it tells you to set one of streamableHTTP, sse, or stdio","Inspect the applied object (kubectl get mcpserver <name> -o yaml) to see what endpoint actually persisted after defaults/pruning","Fix the manifest so exactly one endpoint block carries the URL/command; re-apply","Verify you are on a dapr version whose MCPServer CRD matches the field names you used","After fixing, the operator's hot-reload re-runs registration — confirm the holder connects"],"exampleFix":"# before: no endpoint transport block\napiVersion: dapr.io/v1alpha1\nkind: MCPServer\nmetadata: { name: fs }\nspec:\n  endpoint: {}\n\n# after: streamableHTTP transport set\nspec:\n  endpoint:\n    streamableHTTP:\n      url: http://filesystem-mcp:8080/mcp","handlingStrategy":"validation","validationCode":"// Reject manifests without exactly one transport before registration\nfunc validateTransport(srv *mcpserverapi.MCPServer) error {\n    n := 0\n    for _, set := range []bool{\n        srv.Spec.Endpoint.StreamableHTTP != nil,\n        srv.Spec.Endpoint.SSE != nil,\n        srv.Spec.Endpoint.Stdio != nil,\n    } { if set { n++ } }\n    if n != 1 {\n        return fmt.Errorf(\"MCPServer %q must set exactly one of streamableHTTP, sse, stdio (got %d)\", srv.Name, n)\n    }\n    return nil\n}","typeGuard":"func hasTransport(srv *mcpserverapi.MCPServer) bool {\n    return srv.Spec.Endpoint.StreamableHTTP != nil ||\n        srv.Spec.Endpoint.SSE != nil ||\n        srv.Spec.Endpoint.Stdio != nil\n}","tryCatchPattern":"transport, err := buildTransport(server, httpClient)\nif err != nil {\n    return nil, fmt.Errorf(\"failed to build transport for %q: %w\", server.Name, err)\n    // permanent config error: do not retry; fix the manifest\n}","preventionTips":["Add admission/CI validation that every MCPServer manifest sets exactly one endpoint block","Diff the persisted object (kubectl get -o yaml) after apply to catch pruned/typo'd fields","Pin manifests to the CRD version of the deployed dapr release"],"tags":["configuration","manifest","mcp","transport"],"backgroundTag":null,"analyzedSha":"74ad41702745709bb15fe2114ff693b8c59bc3cc","analyzedAt":"2026-08-16T04:22:26.543Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}