we-promise/sure · warning · ActiveRecord::RecordNotFound
Unknown record type
Error message
Unknown record type
What it means
Settings::BackgroundJobsController#cancel resolves the target record via find_record!: it iterates the four cancellable base types (Sync, Import, ImportSession, FamilyExport), looks up params[:id] in each table, and requires params[:record_type] to equal either the found record's class name or the base class name (STI-safe). If no (id, claimed type) pair matches, it raises ActiveRecord::RecordNotFound("Unknown record type"), which Rails renders as a 404. The design is deliberate: the param is never turned into a constant, so arbitrary class names cannot be reflected on.
Source
Thrown at app/controllers/settings/background_jobs_controller.rb:60
end
end
private
# Resolves record_type without reflecting on request input: rather than
# turning the param into a constant, look the id up in each cancellable
# base table and require the claimed type to match the found record's
# class (or its base class). STI subclass names are still accepted —
# the UI sends base_class names, but a direct request naming e.g.
# TransactionImport shouldn't 404.
def find_record!
claimed_type = params[:record_type].to_s
CANCELLABLE_BASE_TYPES.each do |base|
record = base.find_by(id: params[:id])
return record if record && [ record.class.name, base.name ].include?(claimed_type)
end
raise ActiveRecord::RecordNotFound, "Unknown record type"
end
# User-facing: surfaces as the failed operation's error in the family UI.
def cancelled_error_message
t("settings.background_jobs.cancel.cancelled_error")
end
def cancellable_status?(record)
case record
when Sync then record.in_progress?
when Import then record.importing? || record.reverting?
when ImportSession then record.importing?
when FamilyExport then record.pending? || record.processing?
end
end
def apply_cancel!(record)
case recordView on GitHub (pinned to e69894adb9)
Solutions
- Check that record_type is exactly the base class name (Sync, Import, ImportSession, FamilyExport) or the record's actual class name — the UI always sends base_class names
- Verify the id exists and belongs to the table you named; look it up in the matching model before issuing the cancel request
- If you added a new cancellable type, add its base class to CANCELLABLE_BASE_TYPES in app/controllers/settings/background_jobs_controller.rb:4
- Treat the 404 as authoritative: the job either finished, was removed, or was never cancellable — re-render the console page for fresh state
Example fix
# before (curl/console)
Settings::BackgroundJobsController # e.g. request with record_type: "imports", id: 42
# after: use the exact base class name
# POST /settings/background_jobs/42/cancel with record_type=Import
# or in Ruby:
base = [Sync, Import, ImportSession, FamilyExport].find { |b| b.name == claimed_type }
raise ArgumentError, "bad type" unless base && base.exists?(42) Defensive patterns
Strategy: validation
Validate before calling
# Ruby client, before issuing the cancel request
ALLOWED = %w[Sync Import ImportSession FamilyExport].freeze
def valid_cancel?(record_type, id)
base = ALLOWED.find { |name| name == record_type }
return false unless base
base.constantize.exists?(id)
end Type guard
def cancellable_type?(value) %w[Sync Import ImportSession FamilyExport].include?(value.to_s) end
Try / catch
# In a Rails controller that proxies the cancel call rescue ActiveRecord::RecordNotFound redirect_to settings_background_jobs_path, alert: "Job not found or not cancellable" end
Prevention
- Always send the base class name the UI sends (Sync, Import, ImportSession, FamilyExport)
- Look the record up yourself before issuing cancel; treat a completed job as gone
- Never guess type strings from table names or symbols
- Refresh the background jobs console before acting on stale rows
When it happens
Trigger: POST/PATCH to settings background_jobs cancel path with a record_type that is not one of "Sync"/"Import"/"ImportSession"/"FamilyExport" (or an STI subclass name like "TransactionImport"), a record_type that doesn't match the table where params[:id] actually lives (e.g. record_type=Sync but the id belongs to an ImportSession), or an id that doesn't exist in any of the four tables.
Common situations: Hand-crafted requests or stale UI links after a record was deleted or finished; scripts that guess the type string; passing the model's table name or a downcased symbol ("sync", "imports") instead of the exact class name; an admin bookmarking a cancel URL for a job that has since completed and been removed.
Related errors
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/14cca04d343ea218.
Report an issue: GitHub.