{"record":{"id":"99d65271b893be65","repo":"gastownhall/beads","slug":"invalid-strategy-q-want-q-or-q","errorCode":null,"errorMessage":"invalid strategy %q (want %q or %q)","messagePattern":"invalid strategy %q \\(want %q or %q\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/versioncontrolops/conflicts.go","lineNumber":70,"sourceCode":"}\n\n// ValidateConflictTable rejects anything that is not a plain SQL identifier,\n// so a table name can be safely interpolated into a conflict query. Conflict\n// table names come from dolt_conflicts (or an operator's --table) and MySQL\n// cannot parameterize an identifier, so they are validated instead. It reuses\n// the package's existing table-name gate, which ResolveConflicts already\n// trusts for exactly this.\nfunc ValidateConflictTable(table string) error {\n\treturn validateTableName(table)\n}\n\n// ValidateConflictStrategy accepts only the two dolt strategies.\nfunc ValidateConflictStrategy(strategy string) error {\n\tswitch strategy {\n\tcase ConflictStrategyOurs, ConflictStrategyTheirs:\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid strategy %q (want %q or %q)\", strategy, ConflictStrategyOurs, ConflictStrategyTheirs)\n\t}\n}\n\n// SupportsRowResolve reports whether table can be resolved row by row.\nfunc SupportsRowResolve(table string) bool {\n\t_, ok := conflictRowKeyColumn[table]\n\treturn ok\n}\n\n// splitConflictColumn splits a dolt conflict column into its side and field\n// name. ok is false for columns that belong to no side (dolt does not emit\n// any today, but a future column must not be silently treated as a field).\nfunc splitConflictColumn(col string) (side, field string, ok bool) {\n\tfor _, s := range conflictSides {\n\t\tif strings.HasPrefix(col, s) {\n\t\t\treturn strings.TrimSuffix(s, \"_\"), strings.TrimPrefix(col, s), true\n\t\t}\n\t}","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/versioncontrolops/conflicts.go#L52-L88","documentation":"ValidateConflictStrategy rejects any conflict resolution strategy other than the two dolt row-level strategies, \"ours\" (ConflictStrategyOurs) or \"theirs\" (ConflictStrategyTheirs). The library defines only these two valid values because dolt's row-conflict resolution supports exactly these choices. The error echoes the rejected value and the two accepted values to make the mistake obvious.","triggerScenarios":"Calling ResolveConflictRows, MergeWithStrategy, or TestValidateConflictStrategy with a strategy string that is not exactly ConflictStrategyOurs or ConflictStrategyTheirs — e.g. typos (\"our\", \"theirs \", \"both\", \"manual\"), uppercase variants (\"OURS\"), or an empty string.","commonSituations":"Hard-coding a strategy literal from memory instead of using the exported constants; importing a strategy value from another library (e.g. git merge strategy names like \"ort\" or \"recursive\"); reading the strategy from config/CLI flags without validating against the constants; case mismatch after lowercasing user input fails.","solutions":["Use the exported constants ConflictStrategyOurs or ConflictStrategyTheirs instead of string literals","Normalize input with strings.ToLower(strings.TrimSpace(s)) before passing it","Validate user/config-supplied strategies up front with ValidateConflictStrategy and surface the message to the user","Check for typos against the exact values shown in the error message"],"exampleFix":"// before\nn, err := ResolveConflictRows(ctx, db, \"issues\", keys, \"Ours\")\n// after\nn, err := ResolveConflictRows(ctx, db, \"issues\", keys, versioncontrolops.ConflictStrategyOurs)","handlingStrategy":"validation","validationCode":"if s := strings.ToLower(strings.TrimSpace(strategy)); s != versioncontrolops.ConflictStrategyOurs && s != versioncontrolops.ConflictStrategyTheirs {\n    return fmt.Errorf(\"strategy must be %q or %q, got %q\", versioncontrolops.ConflictStrategyOurs, versioncontrolops.ConflictStrategyTheirs, strategy)\n}","typeGuard":"func isValidStrategy(s string) bool {\n    return s == versioncontrolops.ConflictStrategyOurs || s == versioncontrolops.ConflictStrategyTheirs\n}","tryCatchPattern":"if err := versioncontrolops.ValidateConflictStrategy(strategy); err != nil {\n    return fmt.Errorf(\"bad strategy argument: %w\", err) // invalid input, not transient\n}","preventionTips":["Always pass the exported constants, never raw strings","Normalize and validate user/config input before calling","Add a unit test enumerating valid strategy values"],"tags":["validation","conflict-resolution","dolt"],"backgroundTag":"invalid-enum-value","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}