{"record":{"id":"d46f1fb9b5d725ca","repo":"gastownhall/beads","slug":"reading-types-custom-w","errorCode":null,"errorMessage":"reading types.custom: %w","messagePattern":"reading types\\.custom: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/template.go","lineNumber":609,"sourceCode":"\t\tt := issue.IssueType\n\t\tif t == \"\" || t.IsBuiltIn() {\n\t\t\tcontinue\n\t\t}\n\t\tunknown[t] = true\n\t}\n\tif len(unknown) == 0 {\n\t\treturn nil\n\t}\n\n\t// Match insert validation's sources: the types.custom config value\n\t// (kept in step with the custom_types table by SyncConfigTables)\n\t// overlaid with config.yaml-declared types. Read through s so a\n\t// transaction-bound caller sees in-transaction registration.\n\texisting, err := s.GetConfig(ctx, \"types.custom\")\n\tif err != nil {\n\t\t// Don't degrade to \"nothing registered\": a transient read failure\n\t\t// would silently flatten types the operator did register.\n\t\treturn fmt.Errorf(\"reading types.custom: %w\", err)\n\t}\n\tfor _, t := range issueops.ParseTypesConfigValue(existing) {\n\t\tdelete(unknown, types.IssueType(t))\n\t}\n\tfor _, t := range config.GetCustomTypesFromYAML() {\n\t\tdelete(unknown, types.IssueType(t))\n\t}\n\tif len(unknown) == 0 {\n\t\treturn nil\n\t}\n\n\tnames := make([]string, 0, len(unknown))\n\tfor t := range unknown {\n\t\tnames = append(names, string(t))\n\t}\n\tsort.Strings(names)\n\tWarnError(\"flattening unregistered issue type(s) to task (epic for steps with children): %s (register with bd config set types.custom to keep them)\", strings.Join(names, \", \"))\n","sourceCodeStart":591,"sourceCodeEnd":627,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/template.go#L591-L627","documentation":"flattenUnregisteredIssueTypes determines which issue types in a cloned subgraph are unknown, by reading the registered custom types from config key 'types.custom' via s.GetConfig. If that read fails, the function refuses to assume 'nothing is registered' (which would silently flatten legitimately registered custom types) and instead throws 'reading types.custom: %w'. This is a deliberate fail-loud guard against data loss.","triggerScenarios":"Calling cloneSubgraphInto (or flattenUnregisteredIssueTypes directly, as in TestFlattenUnregisteredIssueTypes) when s.GetConfig(ctx, \"types.custom\") errors — transaction-bound reads failing, storage driver errors, or config store corruption.","commonSituations":"Dolt storage read failure mid-transaction; a broken or unreadable config table after an interrupted migration; embedded driver I/O errors.","solutions":["Inspect the wrapped error from errors.Unwrap and fix the underlying config-read failure.","Run `bd doctor` to check storage/config health.","Retry the clone once storage is responsive.","Declare the custom types in config.yaml so cloning works even when custom-type registration must be re-created."],"exampleFix":"// config.yaml\nissue-types:\n  custom:\n    - molecule\n    - gate\n// ensures types resolve even if types.custom registration needs rebuild","handlingStrategy":"try-catch","validationCode":"if _, err := s.GetConfig(ctx, \"types.custom\"); err != nil {\n\treturn fmt.Errorf(\"cannot read registered types, aborting clone: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"err := flattenUnregisteredIssueTypes(ctx, tx, issues)\nif err != nil && strings.HasPrefix(err.Error(), \"reading types.custom\") {\n\t// do NOT proceed: proceeding would flatten registered custom types\n\treturn err\n}","preventionTips":["Keep the config table healthy; run `bd doctor` after storage errors.","Declare custom types in config.yaml as a resilient fallback.","Avoid interrupting `bd` mid-transaction."],"tags":["storage","config","issue-types","template"],"backgroundTag":"config-read-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}