gastownhall/beads · error

transitioning to intermediate state %q: %w

Error message

transitioning to intermediate state %q: %w

What it means

When a direct state change returns 400 and a multi-hop path exists, transitionWorkItem walks through intermediate states, calling UpdateWorkItem for each. If an intermediate hop fails, the error is wrapped as 'transitioning to intermediate state "<state>"' and the walk aborts; the work item may be left in the last successfully reached intermediate state.

Source

Thrown at internal/ado/statetransition.go:135

	// through intermediate states. Any other error is a real failure.
	var apiErr *APIError
	if !errors.As(err, &apiErr) || apiErr.StatusCode != http.StatusBadRequest {
		return nil, fmt.Errorf("transitioning to %q: %w", targetState, err)
	}

	path := resolveTransitionPath(workItemType, currentState, targetState)
	if len(path) == 0 {
		return nil, fmt.Errorf("no known transition path from %q to %q for %s: %w",
			currentState, targetState, workItemType, err)
	}

	// Walk through intermediate states.
	var lastWI *WorkItem
	for _, intermediate := range path {
		fields := map[string]interface{}{FieldState: intermediate}
		lastWI, err = c.UpdateWorkItem(ctx, workItemID, fields)
		if err != nil {
			return nil, fmt.Errorf("transitioning to intermediate state %q: %w", intermediate, err)
		}
	}
	return lastWI, nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the inner error: if 400, check ADO work item type rules that require fields on the intermediate state and set them via UpdateWorkItem alongside the state change.
  2. Refresh the work item before re-running to clear 409 revision conflicts.
  3. Fix PAT write permissions for 401/403.
  4. Re-run the transition: after the partial walk the current state is a valid intermediate, so direct or shorter-path transitions may now succeed.

Example fix

// before: only setting state on intermediate hops
fields := map[string]interface{}{FieldState: intermediate}
lastWI, err = c.UpdateWorkItem(ctx, workItemID, fields)
// after: include rule-required fields for the intermediate state
fields := map[string]interface{}{
    FieldState: intermediate,
    "System.AssignedTo": assignee, // satisfies rules like 'must be assigned when Active'
}
lastWI, err = c.UpdateWorkItem(ctx, workItemID, fields)
Defensive patterns

Strategy: try-catch

Validate before calling

// Check required-field rules by inspecting the item before intermediate hops
wi, _, err := client.GetWorkItem(ctx, workItemID, nil)
if err == nil && intermediate == "Active" {
    if wi.Fields["System.AssignedTo"] == nil {
        return fmt.Errorf("set System.AssignedTo before moving to Active (process rule)")
    }
}

Type guard

func isIntermediateHopFailure(err error) (string, bool) {
    m := regexp.MustCompile(`transitioning to intermediate state "([^"]+)"`).FindStringSubmatch(err.Error())
    if len(m) < 2 { return "", false }
    return m[1], true
}

Try / catch

wi, err := client.TransitionWorkItem(ctx, id, target)
if err != nil {
    if state, ok := isIntermediateHopFailure(err); ok {
        log.Printf("walk stopped at intermediate state %q; item may need required fields set there", state)
        // recover: refresh item and either set fields or accept the current state
    }
    return err
}

Prevention

When it happens

Trigger: UpdateWorkItem for an intermediate hop returns 401/403, 404, 409 (conflict/stale revision), 500, or even 400 (a rule on the intermediate state blocks the change, e.g. required fields like Microsoft.VSTS.Common.ActivatedDate must be set when moving to Active).

Common situations: ADO process rules requiring fields (assigned-to, iteration path) on the intermediate state; concurrent edits bumping the revision; PAT permissions; ADO outage mid-walk.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/d6e8b29677aefd2d. Report an issue: GitHub.