{"record":{"id":"c0909b67756e2275","repo":"ruby-concurrency/concurrent-ruby","slug":"concurrent-agent-validationerror","errorCode":null,"errorMessage":"Concurrent::Agent::ValidationError","messagePattern":"Concurrent::Agent::ValidationError","errorType":"exception","errorClass":"Concurrent::Agent::ValidationError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/agent.rb","lineNumber":428,"sourceCode":"    # the `:clear_actions` option is give and true, any actions queued on the\n    # Agent that were being held while it was failed will be discarded,\n    # otherwise those held actions will proceed. The `new_value` must pass the\n    # validator if any, or `restart` will raise an exception and the Agent will\n    # remain failed with its old {#value} and {#error}. Observers, if any, will\n    # not be notified of the new state.\n    #\n    # @param [Object] new_value the new value for the Agent once restarted\n    # @param [Hash] opts the configuration options\n    # @option opts [Symbol] :clear_actions true if all enqueued but unprocessed\n    #   actions should be discarded on restart, else false (default: false)\n    # @return [Boolean] true\n    #\n    # @raise [Concurrent:AgentError] when not failed\n    def restart(new_value, opts = {})\n      clear_actions = opts.fetch(:clear_actions, false)\n      synchronize do\n        raise Error.new('agent is not failed') unless failed?\n        raise ValidationError unless ns_validate(new_value)\n        @current.value = new_value\n        @error.value   = nil\n        @queue.clear if clear_actions\n        ns_post_next_job unless @queue.empty?\n      end\n      true\n    end\n\n    class << self\n\n      # Blocks the current thread (indefinitely!) until all actions dispatched\n      # thus far to all the given Agents, from this thread or nested by the\n      # given Agents, have occurred. Will block when any of the agents are\n      # failed. Will never return if a failed Agent is restart with\n      # `:clear_actions` true.\n      #\n      # @param [Array<Concurrent::Agent>] agents the Agents on which to wait\n      # @return [Boolean] true","sourceCodeStart":410,"sourceCodeEnd":446,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/agent.rb#L410-L446","documentation":"`Agent#restart(new_value, opts)` is the recovery path for a failed agent: it first raises `Error('agent is not failed')` unless `failed?`, then validates `new_value` with the agent's `:validate` proc via `ns_validate`, raising Concurrent::Agent::ValidationError when the value is rejected — leaving the agent failed. The validator is the same proc supplied at creation (`Concurrent::Agent.new(0, validate: -> v { v.is_a?(Integer) })`); it is not exposed publicly, so keep your own reference if you need to pre-check values.","triggerScenarios":"`agent.restart('oops')` on an agent created with `validate: -> v { v.is_a?(Integer) }` after it entered a failed state; restarting with a value whose shape differs from the initial one (nil for a numeric-validated agent); restart values taken raw from form params or parsed JSON.","commonSituations":"Error-recovery code that restores a hardcoded default without honoring the validator; validators tightened later so previously accepted restart values now fail; restart payloads built from user input.","solutions":["Pass a value that satisfies the agent's validator — e.g. `agent.restart(Integer(params[:count]))` instead of the raw string.","Keep the validator proc in a constant/reusable lambda, and call it yourself before `restart` to fail fast with a better message.","If the value contract changed, fix the restart payload at its source; relaxing `:validate` requires creating the agent anew.","Guard with `agent.failed?` first so you do not hit the sibling 'agent is not failed' error."],"exampleFix":"// before\nagent.restart(params[:count]) # String from a web form -> ValidationError\n\n// after\nagent.restart(Integer(params[:count]))","handlingStrategy":"try-catch","validationCode":"# keep the validator reusable at creation time\nVALID = -> v { v.is_a?(Integer) }\nagent = Concurrent::Agent.new(0, validate: VALID)\n# before restarting:\nraise ArgumentError, 'invalid restart value' unless agent.failed? && VALID.call(new_value)","typeGuard":null,"tryCatchPattern":"begin\n  agent.restart(new_value)\nrescue Concurrent::Agent::ValidationError\n  agent.restart(0) # known-valid fallback satisfying the validator\nrescue Concurrent::Agent::Error\n  # agent was not failed; nothing to restart\nend","preventionTips":["Define the validator once and reuse it for creation and restart pre-checks.","Coerce restart payloads at the boundary (Integer(), JSON parsing) before they reach the agent.","Check agent.failed? before calling restart.","Cover restart paths in tests with valid and invalid values."],"tags":["concurrent-ruby","agent","restart","validation-error"],"backgroundTag":"value-rejected-by-validator","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}