{"record":{"id":"8cbfcc0185fe644c","repo":"gastownhall/beads","slug":"failed-to-parse-work-item-states-value-w","errorCode":null,"errorMessage":"failed to parse work item states value: %w","messagePattern":"failed to parse work item states value: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/ado/client.go","lineNumber":622,"sourceCode":"\treturn types, nil\n}\n\n// GetWorkItemStates returns the states for a given work item type.\nfunc (c *Client) GetWorkItemStates(ctx context.Context, typeName string) ([]WorkItemState, error) {\n\turlStr := addAPIVersion(c.apiBase() + \"/wit/workitemtypes/\" + url.PathEscape(typeName) + \"/states\")\n\trespBody, err := c.doRequest(ctx, http.MethodGet, urlStr, \"\", nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get work item states: %w\", err)\n\t}\n\n\tvar envelope listResponse\n\tif err := json.Unmarshal(respBody, &envelope); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse work item states response: %w\", err)\n\t}\n\n\tvar states []WorkItemState\n\tif err := json.Unmarshal(envelope.Value, &states); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse work item states value: %w\", err)\n\t}\n\treturn states, nil\n}\n","sourceCodeStart":604,"sourceCodeEnd":626,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/ado/client.go#L604-L626","documentation":"GetWorkItemStates fetched the list of valid states for a work item type from Azure DevOps, but the JSON 'value' array inside the response envelope could not be unmarshaled into []WorkItemState. The HTTP call succeeded, but the response body did not have the expected shape (envelope.value must be a JSON array of state objects with expected field types). This guards callers from a silently nil/partial states list.","triggerScenarios":"Calling GetWorkItemStates against an ADO organization/process whose response 'value' field is not an array of objects matching the WorkItemState struct (e.g. wrong field types like name as object, or value being a single object).","commonSituations":"Pointing ado.url at a proxy/mocked ADO endpoint that returns differently shaped JSON; ADO API version changes altering the states payload; hitting a custom inherited process endpoint that returns extra/renamed fields with incompatible types; misconfigured org URL returning an HTML error page for the states route.","solutions":["Verify the organization URL and API version used by the ADO client match a supported Azure DevOps REST version (check ado.url / AZURE_DEVOPS_URL).","Log/curl the raw response of GET {org}/{project}/_apis/wit/workitemtypes/{type}/states?api-version=... and compare its 'value' entries to the WorkItemState struct field types.","If behind a proxy or mock, fix the mock to return {\"count\":N,\"value\":[{\"name\":..., \"category\":..., ...}]}.","Update the library if Azure DevOps changed the states payload schema."],"exampleFix":"// before: assuming any 200 body is the states envelope\n// after: log the raw body when unmarshal fails\nif err := json.Unmarshal(envelope.Value, &states); err != nil {\n    return nil, fmt.Errorf(\"failed to parse work item states value: %w (body: %.200s)\", err, envelope.Value)\n}","handlingStrategy":"validation","validationCode":"resp, _ := http.Get(statesURL)\nbody, _ := io.ReadAll(resp.Body)\nvar probe struct {\n    Value []map[string]json.RawMessage `json:\"value\"`\n}\nif json.Unmarshal(body, &probe) != nil || len(probe.Value) == 0 && resp.StatusCode == 200 {\n    return fmt.Errorf(\"states endpoint returned unexpected payload\")\n}","typeGuard":"func isValidStatesEnvelope(body []byte) bool {\n    var env struct {\n        Value json.RawMessage `json:\"value\"`\n    }\n    if json.Unmarshal(body, &env) != nil {\n        return false\n    }\n    var states []map[string]interface{}\n    return json.Unmarshal(env.Value, &states) == nil\n}","tryCatchPattern":"states, err := client.GetWorkItemStates(ctx, project, wit)\nif err != nil {\n    var parseErr *json.UnmarshalTypeError\n    if errors.As(err, &parseErr) {\n        log.Printf(\"ADO states payload shape changed: field %s\", parseErr.Field)\n        return fallbackDefaultStates(wit)\n    }\n    return err\n}","preventionTips":["Pin/verify the Azure DevOps api-version used by the client when upgrading.","Validate proxy/mock endpoints return the standard {count, value} envelope.","Smoke-test GetWorkItemStates in CI against the real org before relying on state transitions."],"tags":["json","azure-devops","api-response","deserialization"],"backgroundTag":"schema-validation-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}