charmbracelet/crush · error

failed to create workspace: %w

Error message

failed to create workspace: %w

What it means

CreateWorkspace wraps a failure of the underlying POST request (transport error) with "failed to create workspace". This occurs before a response exists — connection, DNS, timeout, or context cancellation problems while POSTing to /workspaces.

Source

Thrown at internal/client/proto.go:45

		return nil, fmt.Errorf("failed to list workspaces: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("failed to list workspaces: status code %d", rsp.StatusCode)
	}
	var workspaces []proto.Workspace
	if err := json.NewDecoder(rsp.Body).Decode(&workspaces); err != nil {
		return nil, fmt.Errorf("failed to decode workspaces: %w", err)
	}
	return workspaces, nil
}

// CreateWorkspace creates a new workspace on the server.
func (c *Client) CreateWorkspace(ctx context.Context, ws proto.Workspace) (*proto.Workspace, error) {
	ws.ClientID = c.clientID
	rsp, err := c.post(ctx, "/workspaces", nil, jsonBody(ws), http.Header{"Content-Type": []string{"application/json"}})
	if err != nil {
		return nil, fmt.Errorf("failed to create workspace: %w", err)
	}
	defer rsp.Body.Close()
	if err := checkStatus(rsp); err != nil {
		return nil, fmt.Errorf("failed to create workspace: %w", err)
	}
	var created proto.Workspace
	if err := json.NewDecoder(rsp.Body).Decode(&created); err != nil {
		return nil, fmt.Errorf("failed to decode workspace: %w", err)
	}
	return &created, nil
}

// GetWorkspace retrieves a workspace from the server.
func (c *Client) GetWorkspace(ctx context.Context, id string) (*proto.Workspace, error) {
	rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s", id), nil, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get workspace: %w", err)
	}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Check the wrapped error for connection refusal — start or restart the daemon.
  2. Verify the configured server address and port.
  3. Increase the context timeout if creation happens during slow startup.
  4. Check server logs and connectivity (firewall, proxy) between client and daemon.

Example fix

// before
created, err := client.CreateWorkspace(ctx, ws)
if err != nil {
	return err
}
// after: retry transient transport failures with backoff
created, err := client.CreateWorkspace(ctx, ws)
if err != nil && isTransientNetErr(err) {
	ctx2, cancel := context.WithTimeout(ctx, 10*time.Second)
	defer cancel()
	created, err = client.CreateWorkspace(ctx2, ws)
}
if err != nil {
	return err
}
Defensive patterns

Strategy: retry

Validate before calling

// Confirm the daemon accepts connections before creating
conn, err := net.DialTimeout("tcp", serverAddr, 2*time.Second)
if err != nil {
	return fmt.Errorf("daemon not running at %s", serverAddr)
}
conn.Close()
// Validate payload before sending
if ws.Path == "" {
	return errors.New("workspace path is required")
}

Type guard

func isTransportFailure(err error) bool {
	return err != nil && !strings.Contains(err.Error(), "status code")
}

Try / catch

created, err := client.CreateWorkspace(ctx, ws)
if err != nil {
	var netErr net.Error
	if errors.As(err, &netErr) {
		return retryWithBackoff(ctx, 3, func() error {
			created, err = client.CreateWorkspace(ctx, ws)
			return err
		})
	}
	return err
}

Prevention

When it happens

Trigger: Calling CreateWorkspace (via createWorkspaceOnLiveServer) when the HTTP POST cannot be completed: daemon not running, connection refused/reset, request deadline exceeded, or context canceled.

Common situations: Server crashed between a health check and the create call; network interruption; overly tight context timeout during startup; wrong address/port configuration.

Related errors


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