charmbracelet/crush · error

failed to subscribe to events: %w

Error message

failed to subscribe to events: %w

What it means

Wraps errors from c.SubscribeEvents(ctx, ws.ID), which opens the pubsub event stream used to relay streaming output for the run. Thrown when the coordinator event subscription cannot be established.

Source

Thrown at internal/cmd/run.go:255

		return fmt.Errorf("failed to resolve session: %w", err)
	}
	if continueSessionID != "" || useLast {
		slog.Info("Continuing session for non-interactive run", "session_id", sess.ID)
		// If no explicit model override was requested, restore the
		// model/provider from the last assistant message in the
		// session, provided it is still available.
		if largeModel == "" && smallModel == "" {
			if err := restoreModelFromSession(ctx, c, ws, sess.ID); err != nil {
				slog.Warn("Failed to restore model from session", "error", err)
			}
		}
	} else {
		slog.Info("Created session for non-interactive run", "session_id", sess.ID)
	}

	events, err := c.SubscribeEvents(ctx, ws.ID)
	if err != nil {
		return fmt.Errorf("failed to subscribe to events: %w", err)
	}

	// Mint a per-call RunID so we can correlate the terminal
	// RunComplete with *this* SendMessage even if the session was
	// busy and another turn finished first. Without it the stream
	// loop would exit on whichever RunComplete arrived first for
	// the same session and drop the queued prompt's output.
	runID := uuid.New().String()
	if err := c.SendMessage(ctx, ws.ID, sess.ID, runID, prompt); err != nil {
		return fmt.Errorf("failed to send message: %w", err)
	}

	stream := &runStream{
		sessionID: sess.ID,
		runID:     runID,
		out:       os.Stdout,
		read:      make(map[string]int),
	}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Retry the run; transient cancellation usually resolves itself
  2. Ensure the agent/coordinator initialized successfully earlier in the pipeline
  3. Check that the context passed to the command isn't cancelled prematurely (timeouts, signal handling)
  4. Update crush if using it as a library — pubsub wiring changed between versions

Example fix

// before: short timeout kills subscription
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
// after
ctx, cancel := context.WithTimeout(ctx, 5*time.Minute)
events, err := c.SubscribeEvents(ctx, ws.ID)
Defensive patterns

Strategy: retry

Validate before calling

select {
case <-ctx.Done():
	return ctx.Err() // bail before subscribing on a dead context
default:
}

Try / catch

events, err := c.SubscribeEvents(ctx, ws.ID)
if err != nil {
	if errors.Is(err, context.Canceled) {
		return err // don't retry on user cancellation
	}
	select {
	case <-time.After(time.Second):
		events, err = c.SubscribeEvents(ctx, ws.ID)
		if err != nil {
			return fmt.Errorf("failed to subscribe to events: %w", err)
		}
	case <-ctx.Done():
		return ctx.Err()
	}
}

Prevention

When it happens

Trigger: `crush run` calls c.SubscribeEvents(ctx, ws.ID) after session resolution and it returns an error — coordinator/broker unavailable or the workspace context is already cancelled.

Common situations: Parent context cancelled (Ctrl-C, CI job timeout) before subscription; coordinator shut down due to an earlier init failure; internal pubsub wiring failure in an embedded/custom usage of the run command.

Related errors


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