{"record":{"id":"3d17c5eefd559dfc","repo":"gastownhall/beads","slug":"failed-to-parse-projects-response-w-3d17c5","errorCode":null,"errorMessage":"failed to parse projects response: %w","messagePattern":"failed to parse projects response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/linear/client.go","lineNumber":1480,"sourceCode":"\t\t\t\"first\":  MaxPageSize,\n\t\t}\n\t\tif cursor != \"\" {\n\t\t\tvariables[\"after\"] = cursor\n\t\t}\n\n\t\treq := &GraphQLRequest{\n\t\t\tQuery:     projectsQuery,\n\t\t\tVariables: variables,\n\t\t}\n\n\t\tdata, err := c.Execute(ctx, req)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to fetch projects: %w\", err)\n\t\t}\n\n\t\tvar projectsResp ProjectsResponse\n\t\tif err := json.Unmarshal(data, &projectsResp); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to parse projects response: %w\", err)\n\t\t}\n\n\t\tallProjects = append(allProjects, projectsResp.Projects.Nodes...)\n\n\t\tif !projectsResp.Projects.PageInfo.HasNextPage {\n\t\t\tbreak\n\t\t}\n\t\tcursor = projectsResp.Projects.PageInfo.EndCursor\n\t}\n\n\treturn allProjects, nil\n}\n\n// CreateProject creates a new project in Linear.\nfunc (c *Client) CreateProject(ctx context.Context, name, description, state string) (*Project, error) {\n\tquery := `\n\t\tmutation CreateProject($input: ProjectCreateInput!) {\n\t\t\tprojectCreate(input: $input) {","sourceCodeStart":1462,"sourceCodeEnd":1498,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/client.go#L1462-L1498","documentation":"Within FetchProjects' pagination loop, each page's JSON body is unmarshalled into ProjectsResponse. This error wraps a json.Unmarshal failure for a page. The library throws it when the response shape does not match {projects:{nodes:[...],pageInfo:{...}}} — decode failure, not a transport failure.","triggerScenarios":"c.Execute returned HTTP-200 data of unexpected shape: a GraphQL errors envelope instead of data, a gateway error page, or Project/ProjectsResponse/PageInfo structs out of sync with the Linear schema (e.g. field renamed or type changed so encoding/json fails).","commonSituations":"Linear schema change breaking the Project struct; API version mismatch after Linear updates GraphQL; proxy injecting a JSON error object; struct field type mismatch (e.g. progress float vs string).","solutions":["Log/dump the raw `data` for the failing page to see the real shape.","Check for a GraphQL \"errors\" key in the payload and surface its message.","Diff ProjectsResponse/Project/PageInfo structs against the current Linear GraphQL schema and fix drift.","Retry the request directly against the GraphQL endpoint to rule out gateway/proxy body substitution.","Upgrade or pin the client library to match the Linear API version in use."],"exampleFix":"// before\nvar projectsResp ProjectsResponse\nif err := json.Unmarshal(data, &projectsResp); err != nil { return nil, err }\n// after\nvar probe struct{ Projects json.RawMessage `json:\"projects\"`; Errors json.RawMessage `json:\"errors\"` }\n_ = json.Unmarshal(data, &probe)\nif probe.Errors != nil { return nil, fmt.Errorf(\"graphql error: %s\", string(probe.Errors)) }\nvar projectsResp ProjectsResponse\nif err := json.Unmarshal(data, &projectsResp); err != nil { return nil, err }","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isProjectsResponse(data []byte) bool {\n    var probe struct {\n        Projects *struct {\n            Nodes    []json.RawMessage `json:\"nodes\"`\n            PageInfo json.RawMessage   `json:\"pageInfo\"`\n        } `json:\"projects\"`\n    }\n    return json.Unmarshal(data, &probe) == nil && probe.Projects != nil\n}","tryCatchPattern":"projects, err := client.FetchProjects(ctx, state)\nif err != nil {\n    var ute *json.UnmarshalTypeError\n    if errors.As(err, &ute) {\n        return fmt.Errorf(\"projects response shape mismatch at field %s — check Linear schema version\", ute.Field)\n    }\n    return err\n}","preventionTips":["Add a contract test fetching page 1 of projects in CI to detect schema drift.","Log raw bytes on unmarshal failure to see the true payload.","Keep the Project struct minimal (only fields consumed) to reduce drift surface.","Verify gateways don't rewrite 200 responses with error JSON."],"tags":["go","json","unmarshal","pagination","linear-api"],"backgroundTag":"response-unmarshal-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}