gastownhall/beads · error

invalid strategy %q (want %q or %q)

Error message

invalid strategy %q (want %q or %q)

What it means

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.

Source

Thrown at internal/storage/versioncontrolops/conflicts.go:70

}

// ValidateConflictTable rejects anything that is not a plain SQL identifier,
// so a table name can be safely interpolated into a conflict query. Conflict
// table names come from dolt_conflicts (or an operator's --table) and MySQL
// cannot parameterize an identifier, so they are validated instead. It reuses
// the package's existing table-name gate, which ResolveConflicts already
// trusts for exactly this.
func ValidateConflictTable(table string) error {
	return validateTableName(table)
}

// ValidateConflictStrategy accepts only the two dolt strategies.
func ValidateConflictStrategy(strategy string) error {
	switch strategy {
	case ConflictStrategyOurs, ConflictStrategyTheirs:
		return nil
	default:
		return fmt.Errorf("invalid strategy %q (want %q or %q)", strategy, ConflictStrategyOurs, ConflictStrategyTheirs)
	}
}

// SupportsRowResolve reports whether table can be resolved row by row.
func SupportsRowResolve(table string) bool {
	_, ok := conflictRowKeyColumn[table]
	return ok
}

// splitConflictColumn splits a dolt conflict column into its side and field
// name. ok is false for columns that belong to no side (dolt does not emit
// any today, but a future column must not be silently treated as a field).
func splitConflictColumn(col string) (side, field string, ok bool) {
	for _, s := range conflictSides {
		if strings.HasPrefix(col, s) {
			return strings.TrimSuffix(s, "_"), strings.TrimPrefix(col, s), true
		}
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Use the exported constants ConflictStrategyOurs or ConflictStrategyTheirs instead of string literals
  2. Normalize input with strings.ToLower(strings.TrimSpace(s)) before passing it
  3. Validate user/config-supplied strategies up front with ValidateConflictStrategy and surface the message to the user
  4. Check for typos against the exact values shown in the error message

Example fix

// before
n, err := ResolveConflictRows(ctx, db, "issues", keys, "Ours")
// after
n, err := ResolveConflictRows(ctx, db, "issues", keys, versioncontrolops.ConflictStrategyOurs)
Defensive patterns

Strategy: validation

Validate before calling

if s := strings.ToLower(strings.TrimSpace(strategy)); s != versioncontrolops.ConflictStrategyOurs && s != versioncontrolops.ConflictStrategyTheirs {
    return fmt.Errorf("strategy must be %q or %q, got %q", versioncontrolops.ConflictStrategyOurs, versioncontrolops.ConflictStrategyTheirs, strategy)
}

Type guard

func isValidStrategy(s string) bool {
    return s == versioncontrolops.ConflictStrategyOurs || s == versioncontrolops.ConflictStrategyTheirs
}

Try / catch

if err := versioncontrolops.ValidateConflictStrategy(strategy); err != nil {
    return fmt.Errorf("bad strategy argument: %w", err) // invalid input, not transient
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/99d65271b893be65. Report an issue: GitHub.