charmbracelet/crush · error

failed to disable docker MCP: %w

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.

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)

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.