charmbracelet/crush · error

failed to disable docker MCP

Error message

failed to disable docker MCP: %w

What it means

This error is returned by Client.DisableDockerMCP when the HTTP POST to /workspaces/{id}/mcp/docker/disable fails at the transport level before a status code could be read. The root cause is wrapped with %w, so callers can distinguish timeouts, refusals, and cancellations. Like the other transport errors here, it indicates the operation never reached the server successfully.

Solutions

  1. Verify the server is running and the base URL is correct.
  2. Classify the wrapped cause with errors.Is/As and handle timeouts/refusals differently.
  3. Use a context with sufficient deadline for the disable operation.
  4. Retry with backoff for transient network errors.
  5. Check proxy/firewall allow the POST path.

Example fix

// before
ctx := r.Context() // dies with the HTTP handler
client.DisableDockerMCP(ctx, wsID)
// after
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := client.DisableDockerMCP(ctx, wsID); err != nil {
    return fmt.Errorf("disable docker MCP failed (server reachable?): %w", err)
}
Defensive patterns

Strategy: retry

Validate before calling

// Use an independent context so handler cancellation doesn't kill the call
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_ = ctx // pass to DisableDockerMCP

Type guard

func isRetryable(err error) bool {
    var ne net.Error
    if errors.As(err, &ne) { return true }
    return errors.Is(err, context.DeadlineExceeded) || errors.Is(err, syscall.ECONNREFUSED)
}

Try / catch

err := client.DisableDockerMCP(ctx, wsID)
for i := 0; err != nil && isRetryable(err) && i < 3; i++ {
    time.Sleep(time.Duration(1<<i) * time.Second)
    err = client.DisableDockerMCP(ctx, wsID)
}
if err != nil { return err }

Prevention

When it happens

Trigger: Calling DisableDockerMCP(ctx, workspaceID) when the connection fails, DNS lookup fails, the request times out, or the context is cancelled mid-flight.

Common situations: Server shut down during teardown; flaky network in CI pipelines; wrong base URL after migrating environments; short-lived context passed from a request handler that returns early.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/9b5b4cdb8aeb7ad9. Report an issue: GitHub.

Appendix: source

Thrown at internal/client/config.go:280

// EnableDockerMCP enables the Docker MCP server on the workspace.
func (c *Client) EnableDockerMCP(ctx context.Context, id string) error {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/docker/enable", id), nil, nil, nil)
	if err != nil {
		return fmt.Errorf("failed to enable docker MCP: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to enable docker MCP: status code %d", rsp.StatusCode)
	}
	return nil
}

// DisableDockerMCP disables the Docker MCP server on the workspace.
func (c *Client) DisableDockerMCP(ctx context.Context, id string) error {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/docker/disable", id), nil, nil, nil)
	if err != nil {
		return fmt.Errorf("failed to disable docker MCP: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to disable docker MCP: status code %d", rsp.StatusCode)
	}
	return nil
}

// RefreshMCPTools refreshes tools for a named MCP server.
func (c *Client) RefreshMCPTools(ctx context.Context, id, name string) error {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/refresh-tools", id), nil, jsonBody(struct {
		Name string `json:"name"`
	}{Name: name}), http.Header{"Content-Type": []string{"application/json"}})
	if err != nil {
		return fmt.Errorf("failed to refresh MCP tools: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {

View on GitHub (pinned to 7944b8e522)