{"record":{"id":"d0111616ed3ab364","repo":"gastownhall/beads","slug":"db-countforprefix-prefix-must-not-be-empty","errorCode":null,"errorMessage":"db: CountForPrefix: prefix must not be empty","messagePattern":"db: CountForPrefix: prefix must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/issue.go","lineNumber":623,"sourceCode":"\t\treturn false, errors.New(\"db: Exists: id must not be empty\")\n\t}\n\ttable := pickIssueTable(opts.UseWispsTable)\n\t//nolint:gosec // G201: table is one of two hardcoded constants\n\trow := r.runner.QueryRowContext(ctx, fmt.Sprintf(\"SELECT 1 FROM %s WHERE id = ? LIMIT 1\", table), id)\n\tvar one int\n\terr := row.Scan(&one)\n\tif errors.Is(err, sql.ErrNoRows) {\n\t\treturn false, nil\n\t}\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"db: Exists %s: %w\", id, err)\n\t}\n\treturn true, nil\n}\n\nfunc (r *issueSQLRepositoryImpl) CountForPrefix(ctx context.Context, prefix string, opts domain.IssueTableOpts) (int, error) {\n\tif prefix == \"\" {\n\t\treturn 0, errors.New(\"db: CountForPrefix: prefix must not be empty\")\n\t}\n\ttable := pickIssueTable(opts.UseWispsTable)\n\tvar count int\n\t//nolint:gosec // G201: table is one of two hardcoded constants\n\terr := r.runner.QueryRowContext(ctx, fmt.Sprintf(`\n\t\tSELECT COUNT(*)\n\t\tFROM %s\n\t\tWHERE id LIKE CONCAT(?, '-%%')\n\t\t  AND INSTR(SUBSTRING(id, LENGTH(?) + 2), '.') = 0\n\t`, table), prefix, prefix).Scan(&count)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"db: CountForPrefix %s: %w\", prefix, err)\n\t}\n\treturn count, nil\n}\n\nfunc (r *issueSQLRepositoryImpl) NextCounterID(ctx context.Context, prefix string) (int, error) {\n\tif prefix == \"\" {","sourceCodeStart":605,"sourceCodeEnd":641,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/issue.go#L605-L641","documentation":"CountForPrefix() counts issues whose ids share a given prefix; a blank prefix has no meaning and would match nothing or everything depending on the LIKE pattern, so the repository rejects it explicitly. It is an input-validation guard before the COUNT query.","triggerScenarios":"Calling issueSQLRepositoryImpl.CountForPrefix(ctx, \"\", opts) — e.g. a prefix derived from config, CLI flag, or an issue-ID parse that came back empty.","commonSituations":"Missing or empty --prefix style configuration; computing a prefix from a partially parsed ID; a metadata.json field left blank.","solutions":["Supply a non-empty prefix (e.g. \"bd\") before calling","If empty prefix is a valid 'count all' intent, call a list-all/count-all API instead","Log/validate configuration at startup so empty prefixes fail early"],"exampleFix":"// before\nn, err := repo.CountForPrefix(ctx, cfg.IDPrefix, opts)\n// after\nif cfg.IDPrefix == \"\" {\n    return errors.New(\"id prefix not configured\")\n}\nn, err := repo.CountForPrefix(ctx, cfg.IDPrefix, opts)","handlingStrategy":"validation","validationCode":"if prefix == \"\" {\n    return errors.New(\"prefix must be configured before counting issues\")\n}","typeGuard":"func validPrefix(p string) bool { return strings.TrimSpace(p) != \"\" }","tryCatchPattern":"n, err := repo.CountForPrefix(ctx, prefix, opts)\nif err != nil {\n    return fmt.Errorf(\"count prefix %q: %w\", prefix, err)\n}","preventionTips":["Validate the configured ID prefix at application startup","Default the prefix (e.g. \"bd\") when config is empty rather than passing it through","Centralize prefix derivation in one function that enforces non-empty output"],"tags":["go","validation","database","empty-string"],"backgroundTag":"empty-required-argument","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}