{"record":{"id":"29125110d19f69d7","repo":"gastownhall/beads","slug":"db-labelsqlrepository-insert-label-must-not-be-e","errorCode":null,"errorMessage":"db: LabelSQLRepository.Insert: label must not be empty","messagePattern":"db: LabelSQLRepository\\.Insert: label must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/label.go","lineNumber":40,"sourceCode":"\trunner Runner\n\tevents domain.EventsSQLRepository\n}\n\nvar _ domain.LabelSQLRepository = (*labelSQLRepositoryImpl)(nil)\n\nfunc pickLabelTable(useWisps bool) string {\n\tif useWisps {\n\t\treturn \"wisp_labels\"\n\t}\n\treturn \"labels\"\n}\n\nfunc (r *labelSQLRepositoryImpl) Insert(ctx context.Context, issueID, label, actor string, opts domain.LabelOpts) error {\n\tif issueID == \"\" {\n\t\treturn fmt.Errorf(\"db: LabelSQLRepository.Insert: issueID must not be empty\")\n\t}\n\tif label == \"\" {\n\t\treturn fmt.Errorf(\"db: LabelSQLRepository.Insert: label must not be empty\")\n\t}\n\t// Reject an over-length label before the INSERT IGNORE, which would otherwise\n\t// silently truncate it to the VARCHAR(255) column. This is the proxied-server\n\t// (uow) analog of issueops.AddLabelInTx's guard, so both write stacks return a\n\t// typed ErrFieldTooLong instead of storing a label the caller never sent.\n\tif err := types.CheckFieldLen(\"label\", label); err != nil {\n\t\treturn err\n\t}\n\ttable := pickLabelTable(opts.UseWispsTable)\n\t//nolint:gosec // G201: table is one of two hardcoded constants\n\tresult, err := r.runner.ExecContext(ctx,\n\t\tfmt.Sprintf(\"INSERT IGNORE INTO %s (issue_id, label) VALUES (?, ?)\", table),\n\t\tissueID, label,\n\t)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"db: LabelSQLRepository.Insert %s/%s: %w\", issueID, label, err)\n\t}\n\trows, err := result.RowsAffected()","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/label.go#L22-L58","documentation":"Input validation error from LabelSQLRepository.Insert: the label argument is empty. The repository rejects empty labels before INSERT IGNORE, because the database would otherwise silently store an empty/blank label row. This mirrors the issueops.AddLabelInTx guard so both write stacks behave identically.","triggerScenarios":"Calling Insert(ctx, issueID, \"\", actor, opts) — e.g. a label string trimmed to nothing, a missing field in a JSON payload, or whitespace-only input.","commonSituations":"Client sending \"label\": \"\" in an API request, strings.TrimLeft wiping a whitespace label, or a template/config defaulting the label to empty.","solutions":["Validate the label is non-empty (after trimming) before calling Insert","Reject empty label at the API/CLI layer with a clear message","Check upstream parsing for fields dropped or trimmed to empty","Also mind CheckFieldLen: labels over 255 chars are rejected as typed ErrFieldTooLong"],"exampleFix":"// before\nlabel := strings.TrimSpace(input.Label)\nrepo.Insert(ctx, id, label, actor, opts)\n// after\nlabel := strings.TrimSpace(input.Label)\nif label == \"\" {\n    return fmt.Errorf(\"label must not be empty\")\n}\nrepo.Insert(ctx, id, label, actor, opts)","handlingStrategy":"validation","validationCode":"label := strings.TrimSpace(userLabel)\nif label == \"\" || len(label) > 255 {\n    return fmt.Errorf(\"label must be 1-255 chars\")\n}","typeGuard":"func validLabel(s string) bool {\n    t := strings.TrimSpace(s)\n    return t != \"\" && len(t) <= 255\n}","tryCatchPattern":"if err := repo.Insert(ctx, issueID, label, actor, opts); err != nil {\n    var tooLong *types.ErrFieldTooLong\n    if strings.Contains(err.Error(), \"label must not be empty\") || errors.As(err, &tooLong) {\n        return fmt.Errorf(\"invalid label %q: %w\", label, err)\n    }\n    return err\n}","preventionTips":["Trim and validate labels before Insert","Reject empty/overlong labels at the CLI and API layers","Mirror the issueops.AddLabelInTx guards in any custom write path","Test label endpoints with empty and 256+ char inputs"],"tags":["validation","labels","guard-clause"],"backgroundTag":"missing-required-field","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}