ViewComponent/view_component · error · ArgumentError
Cannot deserialize unknown component: #{hash["component_clas
Error message
Cannot deserialize unknown component: #{hash["component_class"]} What it means
Proxy.deserialize rebuilds a deferred component by calling safe_constantize on the stored component_class string from the serialized hash. If that class name no longer resolves — renamed, removed, namespaced differently, or not loadable in the worker process — safe_constantize returns nil and deserialize raises ArgumentError ('Cannot deserialize unknown component: <name>'). This typically surfaces inside a background job or Turbo Stream delivery after a deploy.
Source
Thrown at lib/view_component/serializable/proxy.rb:14
# frozen_string_literal: true
module ViewComponent
module Serializable
# A proxy that wraps a component class and its initialization arguments, deferring
# component instantiation until render time. This allows slot calls and other
# post-initialize configuration to be captured and replayed, and enables the
# proxy itself (rather than a live component instance) to be serialized for
# background jobs (e.g. ActiveJob / Turbo Streams).
class Proxy
# Rebuilds a Proxy from a serialized hash produced by +serialize+
def self.deserialize(hash)
klass = hash["component_class"].safe_constantize
raise ArgumentError, "Cannot deserialize unknown component: #{hash["component_class"]}" unless klass
args = ActiveJob::Arguments.deserialize(hash["initialize_args"] || [])
proxy = new(klass, *args)
Array(hash["slot_calls"]).each do |call|
method_name = call["method"].to_sym
slot_args = ActiveJob::Arguments.deserialize(call["args"])
proxy.public_send(method_name, *slot_args)
end
proxy
end
attr_reader :component_class, :initialize_args, :slot_calls
def initialize(component_class, *args)
if component_class.name.nil?
raise UnserializableError, "Cannot serialize anonymous component class #{component_class.inspect}"View on GitHub (pinned to 9f22c36fa7)
Solutions
- Restore or alias the class name so safe_constantize resolves again (e.g., keep a temporary constant alias during migration).
- Drain or discard stale queued jobs that reference the old class name before/after the rename.
- Verify the class loads in the worker process: 'User::CardComponent'.safe_constantize should not be nil in the job runner's environment.
- Wrap deserialization of stale payloads with a rescue and dead-letter or discard the job (see defense strategy).
Example fix
# before
# job payload enqueued pre-rename: {"component_class" => "User::CardComponent", ...}
ViewComponent::Serializable::Proxy.deserialize(payload)
# -> ArgumentError: Cannot deserialize unknown component: User::CardComponent
# after
# renamed component gets a temporary alias during migration
class CardComponent < ViewComponent::Base; end
User::CardComponent = CardComponent # remove after old jobs drain Defensive patterns
Strategy: try-catch
Validate before calling
hash = {"component_class" => "User::CardComponent", "initialize_args" => [], "slot_calls" => []}
return if hash["component_class"]&.safe_constantize.nil? # drop stale payload early
ViewComponent::Serializable::Proxy.deserialize(hash) Try / catch
begin
proxy = ViewComponent::Serializable::Proxy.deserialize(payload)
rescue ArgumentError => e
raise unless e.message.start_with?("Cannot deserialize unknown component")
Rails.logger.warn("discarding stale component job: #{e.message}")
nil # caller skips enqueueing/rendering
end Prevention
- Avoid renaming component classes that appear in queued render_later payloads, or drain queues around the deploy.
- During migrations, keep a temporary constant alias for the old class name until old jobs are gone.
- Validate hash['component_class'].safe_constantize before deserialize when handling payloads of uncertain age.
When it happens
Trigger: A serialized job payload referencing 'User::CardComponent' after the component was renamed to 'CardComponent'; enqueuing in one deploy and executing in another where the class was deleted; a job worker with different autoload/eager-load configuration where the constant is unavailable; a manually built hash with a typo in component_class.
Common situations: Renaming/refactoring component classes while old render_later jobs are still queued; long-lived queues (Sidekiq, DelayedJob) spanning deploys; zeitwerk namespace changes after moving files; multi-process setups where one environment lacks the component.
Related errors
- Cannot serialize render_later with a block
- Cannot serialize slot call '#{method_name}' with a block
- Cannot serialize anonymous component class #{component_class
- Cannot serialize render_in with a block
- Inline templates can only be defined once per-component.
AI-assisted analysis of ViewComponent/view_component@9f22c36fa7 (2026-08-23).
Data as JSON: /api/errors/1609c36abf1e11ea.
Report an issue: GitHub.