{"record":{"id":"1ddc21b1359a1dab","repo":"gastownhall/beads","slug":"s-1ddc21","errorCode":null,"errorMessage":"%s","messagePattern":"%s","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/linear/mapping.go","lineNumber":380,"sourceCode":"\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)\n\t}\n\n\tvar nameMatches []State","sourceCodeStart":362,"sourceCodeEnd":398,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/mapping.go#L362-L398","documentation":"This error surfaces the missingExplicitStateMapMessage: pushing to Linear requires explicit linear.state_map.* entries because defaults are safe for pulling but too ambiguous for mutations. It fires when config is nil or has no ExplicitStateMap entries, even though the state cache was populated.","triggerScenarios":"Calling ResolveStateIDForBeadsStatus with a populated cache but config == nil or len(config.ExplicitStateMap) == 0 — the user never ran 'bd linear link' or removed the state_map section from configuration.","commonSituations":"Fresh install where pull works with defaults but the first push fails; hand-edited config deleting linear.state_map keys; upgrading from a version that did not require explicit push mappings.","solutions":["Run 'bd linear link' to configure the status mapping interactively.","Add explicit linear.state_map entries to the config mapping each beads status to a Linear state name or type.","Ensure the MappingConfig passed to the resolver is non-nil and its ExplicitStateMap is populated.","Validate the config at startup (ValidatePushStateMappings) before attempting a push.","Check the config file for typos in the linear.state_map key prefix."],"exampleFix":"// before\n# config.toml\n[linear]\napi_key = \"...\"   # no state_map section\n// after\n# config.toml\n[linear.state_map]\nopen = \"Backlog\"\nin_progress = \"In Progress\"\nclosed = \"Done\"","handlingStrategy":"validation","validationCode":"func requireExplicitStateMap(cfg *linear.MappingConfig) error {\n\tif cfg == nil || len(cfg.ExplicitStateMap) == 0 {\n\t\treturn fmt.Errorf(\"linear.state_map not configured; run 'bd linear link' before pushing\")\n\t}\n\treturn nil\n}","typeGuard":"func pushMappingConfigured(cfg *linear.MappingConfig) bool {\n\treturn cfg != nil && len(cfg.ExplicitStateMap) > 0\n}","tryCatchPattern":"stateID, err := linear.ResolveStateIDForBeadsStatus(cache, status, cfg)\nif err != nil && strings.Contains(err.Error(), \"bd linear link\") {\n\treturn fmt.Errorf(\"push blocked: %v\", err)\n}","preventionTips":["Run 'bd linear link' before any push operation in a new workspace.","Validate push mappings at startup (e.g. ValidatePushStateMappings).","Never hand-edit config to remove the [linear.state_map] section.","Treat pull success as insufficient — push requires explicit mappings.","Keep config under version control so deletions are reviewed."],"tags":["linear","configuration","state-mapping","push"],"backgroundTag":"missing-state-map-config","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}