collectiveidea/audited · error · StandardError
invalid action given #{action}
Error message
invalid action given #{action} What it means
This is raised by Audited's Audit#undo (collectiveidea/audited, lib/audited/audit.rb). Audited writes one audit row per change with an action of 'create', 'update', or 'destroy', and #undo reverses exactly those three: a 'create' audit destroys the record, a 'destroy' audit recreates it from audited_changes, and an 'update' audit writes back the old values (audited_changes.transform_values(&:first)). Any other value in the audits.action column falls into the else branch and raises a plain StandardError with the message 'invalid action given <action>'. Because it is a bare StandardError rather than a dedicated error class, you cannot rescue it by class alone without catching everything else.
Source
Thrown at lib/audited/audit.rb:109
(audited_changes || {}).each_with_object({}.with_indifferent_access) do |(attr, values), attrs|
attrs[attr] = (action == "update") ? values.first : values
end
end
# Allows user to undo changes
def undo
case action
when "create"
# destroys a newly created record
auditable.destroy!
when "destroy"
# creates a new record with the destroyed record attributes
auditable_type.constantize.create!(audited_changes)
when "update"
# changes back attributes
auditable.update!(audited_changes.transform_values(&:first))
else
raise StandardError, "invalid action given #{action}"
end
end
# Allows user to be set to either a string or an ActiveRecord object
# @private
def user_as_string=(user)
# reset both either way
self.user_as_model = self.username = nil
user.is_a?(::ActiveRecord::Base) ?
self.user_as_model = user :
self.username = user
end
alias_method :user_as_model=, :user=
alias_method :user=, :user_as_string=
# @private
def user_as_string
user_as_model || usernameView on GitHub (pinned to dbf8432604)
Solutions
- Guard before calling: only invoke undo when audit.action is one of 'create', 'update', 'destroy' (see validationCode) and treat custom actions as non-reversible by undo.
- If you write custom-action audits, reverse them yourself: dispatch on your own action names in application code instead of relying on Audit#undo, or subclass Audited::Audit and extend undo with a when clause for your actions.
- Inspect the offending row and its writer: audit = Audited.audit_class.find(id); check audit.action for casing, whitespace, or nil, then fix the code path that inserted the bad value.
- If rows carry near-canonical legacy values (e.g. 'Update', 'update_attributes'), run a data migration normalizing audits.action to the three canonical strings and add a CHECK constraint (CHECK action IN ('create','update','destroy') or an expanded whitelist) so bad values cannot re-enter.
Example fix
# before audit.undo # => raises StandardError: "invalid action given login" # after UNDOABLE_ACTIONS = %w[create update destroy].freeze return unless UNDOABLE_ACTIONS.include?(audit.action) audit.undo
Defensive patterns
Strategy: validation
Validate before calling
UNDOABLE_ACTIONS = %w[create update destroy].freeze # call before audit.undo return unless UNDOABLE_ACTIONS.include?(audit.action) audit.undo
Type guard
# Returns true only for audits that Audited's #undo knows how to reverse. def undoable?(audit) audit.is_a?(Audited::Audit) && %w[create update destroy].include?(audit.action) end # usage audit.undo if undoable?(audit)
Try / catch
# This error is a plain StandardError (no dedicated class), so guard first
# and rescue narrowly by message prefix, re-raising anything unrelated:
begin
audit.undo
rescue StandardError => e
raise unless e.message.start_with?("invalid action given")
Rails.logger.warn("audit ##{audit.id} action=#{audit.action.inspect} is not undoable; skipping")
end Prevention
- Write only the canonical 'create'/'update'/'destroy' action strings into audits unless you intend to reverse them with your own code.
- Keep a whitelist constant (e.g. UNDOABLE_ACTIONS) and gate every undo/rollback UI and background job on it.
- Add a model validation or DB CHECK constraint on audits.action covering the canonical values plus any custom ones you deliberately use.
- When importing or migrating audits from other systems, normalize the action column to the canonical strings before enabling undo features.
- Never rescue bare StandardError around undo without re-raising unrelated failures — match the 'invalid action given' message prefix instead.
When it happens
Trigger: Calling #undo on an audit whose action string is not exactly 'create', 'update', or 'destroy'. Concrete cases: (1) audits you wrote yourself with a custom action, e.g. user.audits.create(action: 'login', audited_changes: {...}) or Audited.audit_class.create!(auditable: user, action: 'promote'), then audit.undo; (2) case/format variants such as 'Update', 'update_attributes', ' archived ', or nil action from a manual SQL insert or rake task; (3) audit rows imported or migrated from another auditing setup or pre-4.x acts_as_audited data where action verbs differ; (4) an undo/rollback feature that iterates a model's audit history and blindly calls undo on every row.
Common situations: Teams that also store domain events (login, promote, export) in the audits table with custom action strings, then build an admin 'undo' button over audit history. Data migrations importing audits from other systems or old acts_as_audited versions whose action vocabulary differs. Hand-written SQL/rake backfills inserting audits without the canonical action values. Restored or hand-edited audit tables where action was normalized differently (casing, whitespace).
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.
AI-assisted analysis of collectiveidea/audited@dbf8432604 (2026-08-23).
Data as JSON: /api/errors/223e39bdf147a36e.
Report an issue: GitHub.