{"record":{"id":"37d82bd706e5a224","repo":"ruby-concurrency/concurrent-ruby","slug":"update-failed","errorCode":null,"errorMessage":"Update failed","messagePattern":"Update failed","errorType":"exception","errorClass":"Concurrent::ConcurrentUpdateError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb","lineNumber":29,"sourceCode":"      true until compare_and_set(old_value = get, new_value = yield(old_value))\n      new_value\n    end\n\n    def try_update\n      old_value = get\n      new_value = yield old_value\n\n      return unless compare_and_set old_value, new_value\n\n      new_value\n    end\n\n    def try_update!\n      old_value = get\n      new_value = yield old_value\n      unless compare_and_set(old_value, new_value)\n        if $VERBOSE\n          raise ConcurrentUpdateError, \"Update failed\"\n        else\n          raise ConcurrentUpdateError, \"Update failed\", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE\n        end\n      end\n      new_value\n    end\n  end\nend\n","sourceCodeStart":11,"sourceCodeEnd":38,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb#L11-L38","documentation":"`try_update!` on atomic references (AtomicReference and every class mixing in AtomicDirectUpdate) does one optimistic read-yield-compare_and_set cycle. When the CAS loses a race — another thread changed the value between your read and your write — it raises ConcurrentUpdateError ('Update failed'). This specific raise site is the `$VERBOSE` branch: when Ruby warnings are enabled (`ruby -w`, `$VERBOSE = true`), the error carries the full live backtrace so you can find the racing call sites.","triggerScenarios":"Two or more threads calling `ref.try_update! { |v| v + 1 }` on the same AtomicReference under contention; any shared counter/accumulator built on AtomicReference with single-shot update semantics; running the suite with `-W` or `$VERBOSE = true` selects this branch over the cached-backtrace one.","commonSituations":"Hot-path code where `update`'s retry loop was swapped for `try_update!` for micro-benchmarks; metrics counters on shared references; CI configurations that enable warnings and suddenly surface verbose backtraces for the same failure.","solutions":["Switch to `ref.update { |v| ... }` — it retries the CAS internally until it succeeds and never raises for contention.","If single-attempt semantics are required, rescue ConcurrentUpdateError and retry or recompute.","Reduce contention: use AtomicFixnum#increment / AtomicBoolean operations, or shard the counter.","Keep $VERBOSE on in development so this error's backtrace shows the true racing call sites."],"exampleFix":"// before\nref.try_update! { |v| v + 1 } # raises ConcurrentUpdateError under contention\n\n// after\nref.update { |v| v + 1 } # retries internally until the CAS succeeds","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  ref.try_update! { |v| compute(v) }\nrescue Concurrent::ConcurrentUpdateError\n  retry\nend","preventionTips":["Default to ref.update { ... } — it retries internally and never raises for contention.","Use purpose-built atomics (AtomicFixnum#increment) for counters.","Rescue ConcurrentUpdateError specifically, never bare Exception.","Run development with $VERBOSE = true to get real racing call sites from this branch."],"tags":["concurrent-ruby","atomic-reference","concurrentupdateerror","cas","optimistic-concurrency","verbose"],"backgroundTag":"optimistic-lock-contention","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}