{"record":{"id":"e2d0a2e8fd95043d","repo":"gastownhall/beads","slug":"db-getcustomtypes-scan-custom-types-w","errorCode":null,"errorMessage":"db: GetCustomTypes: scan custom_types: %w","messagePattern":"db: GetCustomTypes: scan custom_types: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/config.go","lineNumber":160,"sourceCode":"\t}\n\n\treturn unionWithYAMLCustomTypes(fromDB, config.GetCustomTypesFromYAML()), nil\n}\n\nfunc (r *configSQLRepositoryImpl) readCustomTypesTable(ctx context.Context) ([]string, error) {\n\trows, err := r.runner.QueryContext(ctx, \"SELECT name FROM custom_types ORDER BY name\")\n\tif err != nil {\n\t\tif dberrors.IsTableNotExist(err) {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"db: GetCustomTypes: query custom_types: %w\", err)\n\t}\n\tdefer rows.Close()\n\tvar out []string\n\tfor rows.Next() {\n\t\tvar name string\n\t\tif err := rows.Scan(&name); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"db: GetCustomTypes: scan custom_types: %w\", err)\n\t\t}\n\t\tif name = strings.TrimSpace(name); name != \"\" {\n\t\t\tout = append(out, name)\n\t\t}\n\t}\n\tif err := rows.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"db: GetCustomTypes: read custom_types: %w\", err)\n\t}\n\treturn out, nil\n}\n\nfunc (r *configSQLRepositoryImpl) readCustomTypesConfig(ctx context.Context) ([]string, error) {\n\tvalue, err := r.GetConfig(ctx, \"types.custom\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"db: GetCustomTypes: %w\", err)\n\t}\n\treturn issueops.ParseTypesConfigValue(value), nil\n}","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/config.go#L142-L178","documentation":"This error wraps a rows.Scan failure while iterating the custom_types result set in readCustomTypesTable. The query succeeded but a name column could not be decoded into a string — most commonly because a row holds NULL or a non-string value in the name column.","triggerScenarios":"GetCustomTypes → readCustomTypesTable when rows.Scan(&name) fails: a custom_types row has NULL name, or the driver cannot convert the column value to string.","commonSituations":"Externally inserted custom_types rows with NULL names (bypassing the SyncConfigTables path); schema drift removing NOT NULL from custom_types.name; manual SQL edits to the Dolt database.","solutions":["Inspect custom_types for NULL or non-string name values and delete or repair those rows","Re-insert correct type rows via SetConfig so the normalized table is rewritten cleanly","Re-apply NOT NULL constraints on custom_types.name","Run `bd doctor` to verify custom-type table consistency against the config string","Restore from a known-good backup if the table was corrupted externally"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"rows, _ := db.Query(\"SELECT COUNT(*) FROM custom_types WHERE name IS NULL\")\nvar bad int\nrows.Scan(&bad)\nif bad > 0 {\n\treturn fmt.Errorf(\"%d custom_types rows have NULL name; repair before reading\", bad)\n}","typeGuard":null,"tryCatchPattern":"if _, err := repo.GetCustomTypes(ctx); err != nil && strings.Contains(err.Error(), \"scan\") {\n\t// NULL/malformed name row: delete bad rows and re-sync via SetConfig\n\treturn repairCustomTypes(ctx)\n}","preventionTips":["Insert custom types only via SetConfig so names are validated and non-null","Keep NOT NULL on custom_types.name through migrations","Avoid direct SQL writes into the Dolt database","Run `bd doctor` to verify type-table consistency after external modifications"],"tags":["database","scan","null-value","custom-types"],"backgroundTag":"row-scan-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}