{"record":{"id":"c76d91ce84fd1fa1","repo":"gastownhall/beads","slug":"no-workflow-states-found","errorCode":null,"errorMessage":"no workflow states found","messagePattern":"no workflow states found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/linear/mapping.go","lineNumber":377,"sourceCode":"\tif parsed == status {\n\t\t// ParseBeadsStatus returns StatusOpen for unrecognized strings; do not\n\t\t// treat those as matching built-in open (avoids false \"ambiguous mapping\"\n\t\t// when state_map values are custom status names like \"review\").\n\t\tif parsed == types.StatusOpen && normalizedMapped != \"open\" {\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t}\n\treturn false\n}\n\n// ResolveStateIDForBeadsStatus returns the unique Linear workflow state ID to\n// use when pushing the given beads status. Push only trusts explicit\n// linear.state_map.* entries; defaults are safe for pull but too ambiguous for\n// mutation.\nfunc ResolveStateIDForBeadsStatus(cache *StateCache, status types.Status, config *MappingConfig) (string, error) {\n\tif cache == nil || len(cache.States) == 0 {\n\t\treturn \"\", fmt.Errorf(\"no workflow states found\")\n\t}\n\tif config == nil || len(config.ExplicitStateMap) == 0 {\n\t\treturn \"\", fmt.Errorf(\"%s\", missingExplicitStateMapMessage)\n\t}\n\n\t// Outbound override: an explicit linear.outbound_state_map.<status> entry\n\t// names the exact Linear workflow state to push to and short-circuits the\n\t// name/type matching below. This is the escape hatch when multiple Linear\n\t// states share a type (e.g. \"In Progress\" and \"In Review\" are both\n\t// \"started\") and the type-based fallback would otherwise be ambiguous.\n\tif outboundName, ok := config.OutboundStateMap[strings.ToLower(strings.TrimSpace(string(status)))]; ok {\n\t\twant := strings.ToLower(strings.TrimSpace(outboundName))\n\t\tfor _, state := range cache.States {\n\t\t\tif strings.ToLower(strings.TrimSpace(state.Name)) == want {\n\t\t\t\treturn state.ID, nil\n\t\t\t}\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"linear.outbound_state_map.%s = %q does not match any Linear workflow state\", status, outboundName)","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/mapping.go#L359-L395","documentation":"ResolveStateIDForBeadsStatus returns this when the StateCache is nil or contains no Linear workflow states, so there is nothing to resolve a target state ID against. It guards push operations from mutating Linear without a populated state cache.","triggerScenarios":"Calling ResolveStateIDForBeadsStatus with cache == nil or cache.States empty — e.g. before any workflow-state sync ran, or after a sync that fetched zero states (empty team, wrong team scoping).","commonSituations":"Running 'bd push' to Linear before the initial pull/sync populated the cache; the configured Linear team has no workflow states; a corrupted or wiped cache file.","solutions":["Run the Linear sync/pull first so the StateCache is populated with workflow states.","Verify the configured Linear team actually has workflow states (default teams do).","Check that the state cache file exists and is not empty/corrupted; re-sync if in doubt.","Pass a non-nil *StateCache to ResolveStateIDForBeadsStatus at the call site.","Run 'bd linear link' to (re)initialize the Linear integration."],"exampleFix":"// before\nstateID, err := linear.ResolveStateIDForBeadsStatus(cache, status, cfg) // cache may be nil\n// after\nif cache == nil || len(cache.States) == 0 {\n\treturn fmt.Errorf(\"state cache empty; run sync to fetch Linear workflow states first\")\n}\nstateID, err := linear.ResolveStateIDForBeadsStatus(cache, status, cfg)","handlingStrategy":"validation","validationCode":"func requireStateCache(cache *linear.StateCache) error {\n\tif cache == nil {\n\t\treturn fmt.Errorf(\"state cache not loaded; run sync first\")\n\t}\n\tif len(cache.States) == 0 {\n\t\treturn fmt.Errorf(\"state cache empty; re-run Linear sync to fetch workflow states\")\n\t}\n\treturn nil\n}","typeGuard":"func hasStates(cache *linear.StateCache) bool {\n\treturn cache != nil && len(cache.States) > 0\n}","tryCatchPattern":"stateID, err := linear.ResolveStateIDForBeadsStatus(cache, status, cfg)\nif err != nil {\n\tif strings.Contains(err.Error(), \"no workflow states found\") {\n\t\tif err := syncLinearStates(ctx, client); err != nil {\n\t\t\treturn fmt.Errorf(\"state cache empty and re-sync failed: %w\", err)\n\t\t}\n\t\tstateID, err = linear.ResolveStateIDForBeadsStatus(cache, status, cfg)\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n}","preventionTips":["Always sync/pull from Linear before the first push in a new environment.","Check the configured team has workflow states before integrating.","Validate the cache file is present and non-empty at startup.","Run 'bd linear link' during onboarding to bootstrap the cache.","Guard resolver call sites with an explicit cache-populated check."],"tags":["linear","state-mapping","configuration","sync"],"backgroundTag":"empty-state-cache","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}