charmbracelet/crush · error

cannot continue a child session: %s

Error message

cannot continue a child session: %s

What it means

After a session is fetched successfully, resolveSession rejects it if ParentSessionID is non-empty. Child sessions are created for sub-agents/tasks spawned from a parent run and are not meant to be resumed directly, so continuing one is blocked with this error.

Source

Thrown at internal/cmd/run.go:680

			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

	default:
		return c.CreateSession(ctx, wsID, "non-interactive")

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Find the parent session ID (the child's ParentSessionID field) and pass that to --continue instead.
  2. List sessions and choose one without a parent (a top-level session).
  3. If you need the child's context, resume the parent session which contains the child's history.

Example fix

// before
crush run --continue <child-session-id> "continue"
// after
# resume the parent of that child session instead
crush run --continue <parent-session-id> "continue"
Defensive patterns

Strategy: validation

Validate before calling

sess, err := c.GetSession(ctx, wsID, contID)
if err == nil && sess.ParentSessionID != "" {
    return fmt.Errorf("refusing to continue child session %s; use parent %s", contID, sess.ParentSessionID)
}

Type guard

func isTopLevel(s proto.Session) bool { return s.ParentSessionID == "" }

Try / catch

sess, err := resolveSession(ctx, c, wsID, contID, false)
if err != nil && strings.HasPrefix(err.Error(), "cannot continue a child session:") {
    // resolve the parent and retry
    parentID := childToParent[contID]
    sess, err = resolveSession(ctx, c, wsID, parentID, false)
}

Prevention

When it happens

Trigger: Calling `crush run --continue <childSessionID>` where the session record has a non-empty ParentSessionID, i.e. the ID belongs to a task/sub-agent session rather than a top-level session.

Common situations: Copying a session ID from a child task in the session list or logs instead of the parent; picking the wrong row when several sessions share a similar name; scripting against session IDs harvested from internal message records.

Related errors


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