{"record":{"id":"f695b036b9230ab9","repo":"gastownhall/beads","slug":"resolve-s-conflicts-with-s-strategy-w","errorCode":null,"errorMessage":"resolve %s conflicts with '%s' strategy: %w","messagePattern":"resolve (.+?) conflicts with '(.+?)' strategy: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/versioncontrolops/mergesettle.go","lineNumber":167,"sourceCode":"\tif !resolved {\n\t\tif conflicts, err := GetConflicts(ctx, db); err == nil && len(conflicts) > 0 {\n\t\t\tif strategy == \"\" {\n\t\t\t\tabortMerge(ctx, db, preMergeClean)\n\t\t\t\treturn &MergeConflictsError{Conflicts: conflicts, MergeErr: mergeErr}\n\t\t\t}\n\t\t\t// #4992 part 2: the operator asked for an escape hatch. Unlike\n\t\t\t// TryAutoResolveMergeConflicts, no allowlist applies — every\n\t\t\t// conflicted table (the resolver pre-screens ALL of them before\n\t\t\t// resolving any, so `resolved == false` means none were touched)\n\t\t\t// is resolved with the named strategy.\n\t\t\tfor _, c := range conflicts {\n\t\t\t\ttable := c.Field\n\t\t\t\tif table == \"\" {\n\t\t\t\t\ttable = \"issues\"\n\t\t\t\t}\n\t\t\t\tif err := ResolveConflicts(ctx, db, table, strategy); err != nil {\n\t\t\t\t\tabortMerge(ctx, db, preMergeClean)\n\t\t\t\t\treturn fmt.Errorf(\"resolve %s conflicts with '%s' strategy: %w\", table, strategy, err)\n\t\t\t\t}\n\t\t\t\tif _, err := db.ExecContext(ctx, \"CALL DOLT_ADD(?)\", table); err != nil {\n\t\t\t\t\tabortMerge(ctx, db, preMergeClean)\n\t\t\t\t\treturn fmt.Errorf(\"stage resolved %s: %w\", table, err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tstrategyResolved = true\n\t\t}\n\t}\n\n\t// bd-6dnrw.4: repair FK cascade violations the merge produced (child rows\n\t// whose parent issue was deleted on the other clone). Unrepaired\n\t// violations MUST NOT survive: with the force flag on, every statement\n\t// autocommits, so the abort below is what keeps them out of the database.\n\t// This also covers violations a strategy resolution left behind (e.g.\n\t// --ours keeps a child row whose parent was deleted on the other side).\n\trepairedViol, hadViol, violErr := TryRepairFKCascadeViolations(ctx, db)\n\tif violErr != nil {","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/versioncontrolops/mergesettle.go#L149-L185","documentation":"In SettleMerge's operator escape-hatch path (--strategy ours|theirs), each conflicted table is resolved via ResolveConflicts. \"resolve %s conflicts with '%s' strategy: %w\" wraps a per-table resolution failure. The merge is aborted and the working set restored before this error is returned, so nothing is left half-resolved.","triggerScenarios":"Calling MergeAndSettleWithStrategy (or MergeAndSettle via MergeAndSettle → WithStrategy \"\") with a strategy whose resolution fails for a given table — e.g. an invalid strategy string reaching ResolveConflicts, or Dolt rejecting DOLT_CONFLICTS_RESOLVE because the conflict set changed mid-loop.","commonSituations":"Passing an unvalidated strategy (not validated in this path — validate with ValidateConflictStrategy first); another session resolving the same conflicts concurrently; Dolt version quirks in DOLT_CONFLICTS_RESOLVE argument handling.","solutions":["Validate the strategy with ValidateConflictStrategy(\"ours\"|\"theirs\") before calling","Re-run the merge: the abort restored the working set, so a retry with a corrected strategy is safe","Check for concurrent sessions touching the same database during the merge","Inspect the wrapped error for the specific DOLT_CONFLICTS_RESOLVE failure reason"],"exampleFix":"// before: unvalidated strategy reaches resolution\nerr := versioncontrolops.MergeAndSettleWithStrategy(ctx, db, ref, \"mine\")\n// after\nif err := versioncontrolops.ValidateConflictStrategy(\"theirs\"); err != nil {\n\treturn err\n}\nerr = versioncontrolops.MergeAndSettleWithStrategy(ctx, db, ref, \"theirs\")","handlingStrategy":"validation","validationCode":"// validate before calling MergeAndSettleWithStrategy\nif strategy != \"\" && strategy != \"ours\" && strategy != \"theirs\" {\n\treturn fmt.Errorf(\"strategy must be ours or theirs, got %q\", strategy)\n}\n// or use the library's validator:\nif err := versioncontrolops.ValidateConflictStrategy(strategy); err != nil { return err }","typeGuard":"func validStrategy(s string) bool { return s == \"\" || s == \"ours\" || s == \"theirs\" }","tryCatchPattern":"err := versioncontrolops.MergeAndSettleWithStrategy(ctx, db, ref, strategy)\nif err != nil {\n\tvar mce *versioncontrolops.MergeConflictsError\n\tif errors.As(err, &mce) {\n\t\t// conflicts need operator attention; merge was aborted\n\t} else if strings.Contains(err.Error(), \"with '\") {\n\t\t// strategy resolution failed; retry after fixing strategy/session\n\t}\n\treturn err\n}","preventionTips":["Always route user-supplied strategy strings through ValidateConflictStrategy","Avoid concurrent bd sessions during pull/merge","Retry once after a resolution failure — the abort restores the working set","Keep Dolt versions aligned across federation clones"],"tags":["dolt","merge-conflicts","strategy"],"backgroundTag":"merge-conflict-resolution-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}