charmbracelet/crush · error

session not found: %s

Error message

session not found: %s

What it means

resolveSession wraps any error from client.GetSession as "session not found: %s" when --continue is given a session ID. It intentionally masks the underlying error (network failure, RPC error, or truly missing session) into a single message, so any failure fetching the session surfaces as 'not found'.

Source

Thrown at internal/cmd/run.go:677

		}
		return modelMatch{}, fmt.Errorf(
			"%s model: model %q found in multiple providers: %s. Please specify provider using 'provider/model' format",
			label, modelID, strings.Join(names, ", "),
		)
	}
	return matches[0], nil
}

// resolveSession returns the session to use for a non-interactive run.
// If continueSessionID is set it fetches that session; if useLast is set it
// returns the most recently updated top-level session; otherwise it creates a
// new one.
func resolveSession(ctx context.Context, c *client.Client, wsID, continueSessionID string, useLast bool) (*proto.Session, error) {
	switch {
	case continueSessionID != "":
		sess, err := c.GetSession(ctx, wsID, continueSessionID)
		if err != nil {
			return nil, fmt.Errorf("session not found: %s", continueSessionID)
		}
		if sess.ParentSessionID != "" {
			return nil, fmt.Errorf("cannot continue a child session: %s", continueSessionID)
		}
		return sess, nil

	case useLast:
		sessions, err := c.ListSessions(ctx, wsID)
		if err != nil || len(sessions) == 0 {
			return nil, fmt.Errorf("no sessions found to continue")
		}
		last := sessions[0]
		for _, s := range sessions[1:] {
			if s.UpdatedAt > last.UpdatedAt && s.ParentSessionID == "" {
				last = s
			}
		}
		return &last, nil

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Run `crush session list` (or list sessions via the client) to find a valid, existing session ID and copy it exactly.
  2. Verify you are pointing at the same workspace/data directory (same --data-dir) where the session was created.
  3. Check that the Crush server is running and reachable; a connection error is reported as 'session not found'.
  4. If the session is genuinely gone, drop --continue and start a new session, or use --continue with no ID (useLast).

Example fix

// before
crush run --continue 0a1b2c3d "fix the bug"
// after
crush session list            # confirm the real ID
crush run --continue 0a1b2c3d-4e5f-... "fix the bug"
Defensive patterns

Strategy: validation

Validate before calling

sessions, _ := c.ListSessions(ctx, wsID)
var exists bool
for _, s := range sessions {
    if s.ID == wantID && s.ParentSessionID == "" {
        exists = true
        break
    }
}
if !exists {
    return fmt.Errorf("skip --continue %s: no such top-level session in this workspace", wantID)
}

Type guard

func sessionExists(sessions []proto.Session, id string) bool {
    for _, s := range sessions {
        if s.ID == id {
            return true
        }
    }
    return false
}

Try / catch

sess, err := resolveSession(ctx, c, wsID, contID, useLast)
if err != nil {
    if strings.HasPrefix(err.Error(), "session not found:") {
        log.Warnf("session %s unavailable, creating a new one", contID)
        sess, err = c.CreateSession(ctx, wsID, "non-interactive")
    }
    if err != nil {
        return err
    }
}

Prevention

When it happens

Trigger: Running `crush run --continue <sessionID>` (or the equivalent API flag) where client.GetSession(ctx, wsID, continueSessionID) returns an error: the session ID does not exist, belongs to a different workspace ID, was deleted, or the server/connection failed.

Common situations: Typo or truncated session ID passed to --continue; session created under a different workspace or data-dir (pointing at a fresh DB); session cleaned up by retention; Crush server restarted with an empty data directory.

Related errors


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