{"record":{"id":"805f183440f32948","repo":"gastownhall/beads","slug":"linear-state-map-type-fallback-is-ambiguous-for-be","errorCode":null,"errorMessage":"linear.state_map type fallback is ambiguous for beads status %q across Linear states: %s. Set linear.outbound_state_map.%s = \"<state name>\" to disambiguate","messagePattern":"linear\\.state_map type fallback is ambiguous for beads status %q across Linear states: (.+?)\\. Set linear\\.outbound_state_map\\.(.+?) = \"<state name>\" to disambiguate","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/linear/mapping.go","lineNumber":431,"sourceCode":"\t\treturn \"\", fmt.Errorf(\"linear.state_map maps beads status %q to multiple Linear states: %s\", status, strings.Join(names, \", \"))\n\t}\n\n\tvar typeMatches []State\n\tfor _, state := range cache.States {\n\t\tmapped, ok := config.ExplicitStateMap[strings.ToLower(strings.TrimSpace(state.Type))]\n\t\tif ok && stateMapMatchesStatus(mapped, status) {\n\t\t\ttypeMatches = append(typeMatches, state)\n\t\t}\n\t}\n\tif len(typeMatches) == 1 {\n\t\treturn typeMatches[0].ID, nil\n\t}\n\tif len(typeMatches) > 1 {\n\t\tnames := make([]string, 0, len(typeMatches))\n\t\tfor _, state := range typeMatches {\n\t\t\tnames = append(names, state.Name)\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"linear.state_map type fallback is ambiguous for beads status %q across Linear states: %s. Set linear.outbound_state_map.%s = \\\"<state name>\\\" to disambiguate\", status, strings.Join(names, \", \"), status)\n\t}\n\n\treturn \"\", fmt.Errorf(\"linear.state_map has no configured Linear state for beads status %q\", status)\n}\n\n// ParseBeadsStatus converts a status string to types.Status.\nfunc ParseBeadsStatus(s string) types.Status {\n\tswitch strings.ToLower(s) {\n\tcase \"open\":\n\t\treturn types.StatusOpen\n\tcase \"in_progress\", \"in-progress\", \"inprogress\":\n\t\treturn types.StatusInProgress\n\tcase \"blocked\":\n\t\treturn types.StatusBlocked\n\tcase \"closed\", \"done\":\n\t\treturn types.StatusClosed\n\tcase \"deferred\":\n\t\treturn types.StatusDeferred","sourceCodeStart":413,"sourceCodeEnd":449,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/mapping.go#L413-L449","documentation":"No state name matched, so the resolver fell back to matching by Linear state type (via linear.state_map.<type> entries), but multiple Linear states share that type (e.g. 'In Progress' and 'In Review' are both 'started'). The error lists the candidates and tells the user exactly how to disambiguate with an outbound_state_map override.","triggerScenarios":"Calling ResolveStateIDForBeadsStatus where the explicit state-name pass found nothing, but the type-fallback pass matched 2+ states whose linear.state_map.<state-type> entry matches the status.","commonSituations":"Workspace with custom workflow states sharing a type ('Started': 'In Progress', 'Code Review', 'QA'); defaults relying on type matching for pull being reused for push.","solutions":["Set linear.outbound_state_map.<status> = \"<state name>\" to name the exact push target, as the error message instructs.","Add a name-level linear.state_map entry for one specific state instead of the shared type.","Restructure the Linear workflow so the target status is unique per type if you control the workflow.","Re-run 'bd linear link' to generate an unambiguous mapping."],"exampleFix":"// before\n[linear.state_map]\nstarted = \"in_progress\"   # matches both 'In Progress' and 'In Review'\n// after\n[linear.state_map]\nstarted = \"in_progress\"\n[linear.outbound_state_map]\nin_progress = \"In Progress\"","handlingStrategy":"fallback","validationCode":"func needsOutboundOverride(cfg *linear.MappingConfig, cache *linear.StateCache) bool {\n\tcounts := map[string]int{}\n\tfor _, s := range cache.States {\n\t\tif _, ok := cfg.ExplicitStateMap[strings.ToLower(strings.TrimSpace(s.Type))]; ok {\n\t\t\tcounts[strings.ToLower(s.Type)]++\n\t\t}\n\t}\n\tfor t, n := range counts {\n\t\tif n > 1 {\n\t\t\tfmt.Printf(\"type %q matches %d states; configure linear.outbound_state_map\\n\", t, n)\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}","typeGuard":null,"tryCatchPattern":"stateID, err := linear.ResolveStateIDForBeadsStatus(cache, status, cfg)\nif err != nil && strings.Contains(err.Error(), \"Set linear.outbound_state_map\") {\n\treturn fmt.Errorf(\"push blocked; %v\", err) // surface the built-in remediation hint\n}","preventionTips":["Whenever multiple Linear states share a type, configure linear.outbound_state_map for affected statuses.","Prefer name-level state_map entries over type-level in custom workflows.","Run 'bd linear link' which detects ambiguity and prompts for overrides.","Keep type fallback as a pull-only convenience; never rely on it for push.","Audit the workflow after adding states like 'Code Review' that duplicate an existing type."],"tags":["linear","configuration","state-mapping","ambiguity"],"backgroundTag":"ambiguous-state-mapping","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}