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
- 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.
- Refresh the work item before re-running to clear 409 revision conflicts.
- Fix PAT write permissions for 401/403.
- 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
- Populate rule-required fields (assignee, dates, iteration) when walking through states like Active/Resolved.
- Refresh the work item before the walk to avoid revision-conflict 409s.
- Test the transition path against your ADO process in a sandbox project.
- Handle partial walks: after failure the item is in a valid intermediate state; re-running resumes from there.
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
- transitioning to %q: %w
- remove relation %d: %w
- add link %s to %d: %w
- work item %d: %w
- no known transition path from %q to %q for %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/d6e8b29677aefd2d.
Report an issue: GitHub.