{"record":{"id":"a76f7c338865ac73","repo":"charmbracelet/crush","slug":"failed-to-start-docker-mcp-w","errorCode":null,"errorMessage":"failed to start docker MCP: %w","messagePattern":"failed to start docker MCP: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/config.go","lineNumber":235,"sourceCode":"}\n\n// EnableDockerMCP validates Docker MCP availability, stages the\n// configuration, starts the MCP client, and persists the config.\nfunc (b *Backend) EnableDockerMCP(ctx context.Context, workspaceID string) error {\n\tws, err := b.GetWorkspace(workspaceID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tmcpConfig, err := ws.Cfg.PrepareDockerMCPConfig()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := mcptools.InitializeSingle(ctx, config.DockerMCPName, ws.Cfg); err != nil {\n\t\tdisableErr := mcptools.DisableSingle(ws.Cfg, config.DockerMCPName)\n\t\tws.Cfg.RemoveDockerMCPInMemory()\n\t\treturn fmt.Errorf(\"failed to start docker MCP: %w\", errors.Join(err, disableErr))\n\t}\n\n\tif err := ws.Cfg.PersistDockerMCPConfig(mcpConfig); err != nil {\n\t\tdisableErr := mcptools.DisableSingle(ws.Cfg, config.DockerMCPName)\n\t\tws.Cfg.RemoveDockerMCPInMemory()\n\t\treturn fmt.Errorf(\"docker MCP started but failed to persist configuration: %w\", errors.Join(err, disableErr))\n\t}\n\n\tpublishConfigChanged(ws)\n\treturn nil\n}\n\n// DisableDockerMCP closes the Docker MCP client, removes the\n// configuration, and persists the change.\nfunc (b *Backend) DisableDockerMCP(workspaceID string) error {\n\tws, err := b.GetWorkspace(workspaceID)\n\tif err != nil {\n\t\treturn err","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/backend/config.go#L217-L253","documentation":"EnableDockerMCP starts the Docker MCP tool in a workspace and rolls it back (DisableSingle + RemoveDockerMCPInMemory) if startup fails. This error wraps both the original startup error and any rollback disable error via errors.Join, so the message may contain multiple causes. It means the Docker MCP server could not be initialized and the workspace config was reverted to its pre-enable state.","triggerScenarios":"Calling EnableDockerMCP(workspaceID, mcpConfig) when mcptools.InitializeSingle fails — e.g. the docker MCP binary/command defined in mcpConfig is missing, fails to launch, the docker daemon is unavailable, or the MCP handshake times out; the wrapped disableErr additionally appears if DisableSingle itself also fails during rollback.","commonSituations":"Docker not running or not installed on the host; an invalid MCP command/args in the submitted config; permission denied executing the MCP binary; stale workspace config referencing a removed tool; concurrent config edits causing the rollback disable to also error.","solutions":["Verify Docker is installed and the daemon is running (docker info) before enabling.","Check the joined inner errors: the first is the InitializeSingle failure, the second (if any) the rollback failure — fix the root cause first.","Validate the mcpConfig command/args/env values; test the command manually in a shell.","Retry EnableDockerMCP after correcting config; confirm no partial state remains via RemoveDockerMCPInMemory semantics or a fresh workspace read."],"exampleFix":"// before\nif err := mcptools.InitializeSingle(ctx, config.DockerMCPName, ws.Cfg); err != nil { /* opaque failure */ }\n// after\n// log/inspect the joined error to separate startup vs rollback causes, and pre-validate docker:\nif _, err := exec.LookPath(\"docker\"); err != nil {\n    return fmt.Errorf(\"docker binary not found: %w\", err)\n}\nif err := mcptools.InitializeSingle(ctx, config.DockerMCPName, ws.Cfg); err != nil { ... }","handlingStrategy":"try-catch","validationCode":"if _, err := exec.LookPath(\"docker\"); err != nil {\n    return fmt.Errorf(\"docker binary not found: %w\", err)\n}\nif err := ws.Cfg.ValidateMCPConfig(mcpConfig); err != nil {\n    return fmt.Errorf(\"invalid mcp config: %w\", err)\n}","typeGuard":"func IsDockerMCPStartupErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"failed to start docker MCP\")\n}","tryCatchPattern":"if err := backend.EnableDockerMCP(ctx, wsID, cfg); err != nil {\n    var joined interface{ Unwrap() []error }\n    if errors.As(err, &joined) {\n        for _, e := range joined.Unwrap() { log.Printf(\"cause: %v\", e) }\n    }\n    return fmt.Errorf(\"docker MCP enable aborted: %w\", err)\n}","preventionTips":["Pre-flight docker availability (docker info) before enabling.","Validate MCP command/args against a schema before submission.","Keep workspace config access serialized across goroutines/processes.","Log the joined error list separately to distinguish startup vs rollback causes."],"tags":["mcp","docker","config","go"],"backgroundTag":"mcp-server-start-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}