charmbracelet/crush · error

failed to create session for non-interactive mode: %w

Error message

failed to create session for non-interactive mode: %w

What it means

RunNonInteractive resolves the session via app.resolveSession (creating a new session, or continuing an existing one given continueSessionID/useLast); any error is wrapped with this message. Session creation touches the SQLite store, so this usually indicates a database problem rather than an LLM issue.

Source

Thrown at internal/app/app.go:330

	}

	// Non-interactive runs get a single shot at the tool palette, so wait for
	// MCP initialization to settle before reading MCP tools. The coordinator
	// waits again for the same reason (it is the gate the client/server path
	// goes through); doing it here too surfaces the failure before we create a
	// session, and lets the UpdateModels below see every MCP tool.
	if err := mcp.WaitForInit(ctx); err != nil {
		return fmt.Errorf("failed to wait for MCP initialization: %w", err)
	}

	// force update of agent models before running so mcp tools are loaded
	app.AgentCoordinator.UpdateModels(ctx)

	defer stopSpinner()

	sess, err := app.resolveSession(ctx, continueSessionID, useLast)
	if err != nil {
		return fmt.Errorf("failed to create session for non-interactive mode: %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 := app.restoreModelFromSession(ctx, 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)
	}

	// Automatically approve all permission requests for this non-interactive
	// session.

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Verify the DB file location is writable (check $TMPDIR/$XDG_CACHE_HOME paths)
  2. Pass a valid --continue session id (list sessions via `crush sessions`) or omit it to start fresh
  3. Move/delete the corrupt DB file so migrations run again on a fresh DB
  4. Upgrade crush if the DB was written by an incompatible version

Example fix

// before
crush run --continue abc123-definitely-stale "continue"
// after
crush sessions // find a valid id
crush run --continue 8f2c... "continue"
Defensive patterns

Strategy: validation

Validate before calling

if continueSessionID != "" {
	sessions := listSessions() // e.g. via `crush sessions`
	if !contains(sessions, continueSessionID) {
		return fmt.Errorf("unknown session id: %s", continueSessionID)
	}
}
if _, err := os.Stat(dbPath); err != nil || !writable(dbPath) {
	return fmt.Errorf("session DB not writable: %s", dbPath)
}

Try / catch

if err := app.RunNonInteractive(ctx, w, prompt, "", "", true, contID, useLast); err != nil {
	if strings.Contains(err.Error(), "failed to create session") {
		// retry once without continue to start a fresh session
		return app.RunNonInteractive(ctx, w, prompt, "", "", true, "", false)
	}
	return err
}

Prevention

When it happens

Trigger: resolveSession fails: the SQLite DB cannot be opened/migrated, the continueSessionID does not exist, or the 'use last session' lookup fails.

Common situations: Read-only or full disk where the crush DB lives; corrupted ~/.cache/crush DB after an abrupt kill; passing a stale --continue session id that was deleted; schema migration failure after a version upgrade.

Related errors


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