Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by AuroraNomad246

How do I fix the ActiveStorage ArgumentError when uploading file attachments in my Rails API?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

I'm building a Rails API that uses ActiveStorage to attach files to digital forms. However, when I try to POST a file attachment, I get an error about an unexpected Parameters hash. My routes are configured as follows:

RUBY
resources :forms, defaults: { format: :json } do resources :attachments, except: [ :update ] end

My create endpoint in the attachments controller looks like this:

RUBY
def create @form = Form.find(params[:form_id]) if params[:attachment].present? if @form.update(attachments: params[:form][:attachment]) render json: { message: "Attachment uploaded successfully", file_url: rails_blob_path(@form.attachments.last, only_path: true) }, status: :created else render json: form.errors, status: :unprocessable_entity end else render json: { error: "No attachment uploaded" }, status: :unprocessable_entity end end

I have a factory definition for creating forms with attachments:

RUBY
FactoryBot.define do factory :form do user_email { "hello.world@example.com" } status { "started" } trait :with_attachment do after (:create) do |form| form.attachments.attach( io: Rails.root.join("spec", "files", "upload_test_file.pdf").open, filename: "upload_test_file.pdf", content_type: "application/pdf" ) end end end end

And my spec test is written as follows:

RUBY
let(:valid_file) { fixture_file_upload(Rails.root.join("spec", "files", "upload_test_file.pdf"), "application/pdf") } let(:myform) { FactoryBot.create(:form) } ... describe "POST /create" do it "creates a new Form Attachment" do post "/forms/#{myform.id}/attachments", params: { form: { attachment: valid_file } }, headers: valid_headers, as: :json myform.reload expect(myform.attachments).to be_attached expect(response).to have_http_status(:created) end end

The error I encounter is as follows:

BASH
1) /forms/:form_id/attachments POST /create creates a new Form Attachment Failure/Error: if @form.update(attachments: params[:form][:attachment]) ArgumentError: Could not find or build blob: expected attachable, got #<ActionController::Parameters {"content_type" => "application/pdf", "original_filename" => "upload_test_file.pdf", "tempfile" => "#<File:0x00007f38379ef7c0>"} permitted: false>

I suspect the issue is related to sending the file upload data as JSON (using as: :json in the test), which converts the file into a Parameters hash instead of an instance of ActionDispatch::Http::UploadedFile that ActiveStorage expects. I've reviewed some controller examples and confirmed that my model and factory setups are working as intended. How can I adjust my request or endpoint so that ActiveStorage properly receives the file upload?

0

Comments 0

Answers 3

Views 73

3 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

The error you're encountering happens because you're sending the file upload data as JSON instead of as a multipart/form-data request. When you use the option as: :json in your test, the file gets converted into a Parameters hash rather than an instance of ActionDispatch::Http::UploadedFile. ActiveStorage expects the latter format as its “attachable.”

How to Fix

Remove the as: :json option (or switch to a proper multipart request) so that the file is correctly interpreted.

For example, in your spec change:

RUBY
post "/forms/#{myform.id}/attachments", params: { form: { attachment: valid_file } }, headers: valid_headers, as: :json

to

RUBY
post "/forms/#{myform.id}/attachments", params: { form: { attachment: valid_file } }, headers: valid_headers

This lets Rails use the default multipart encoding for file uploads.

Additional Tips

  1. Parameter Check:
    Your controller is checking for params[:attachment].present? but then later calling params[:form][:attachment]. It might be more consistent to check the same key you’re using:

    RUBY
    def create @form = Form.find(params[:form_id]) if params[:form] && params[:form][:attachment].present? if @form.update(attachments: params[:form][:attachment]) render json: { message: "Attachment uploaded successfully", file_url: rails_blob_path(@form.attachments.last, only_path: true) }, status: :created else render json: @form.errors, status: :unprocessable_entity end else render json: { error: "No attachment uploaded" }, status: :unprocessable_entity end end
  2. Strong Parameters:
    Although you're not explicitly using strong parameters here, it's often a good idea to permit parameters for file uploads, ensuring that only the allowed parameters are passed to update.

  3. Factory Setup:
    Your FactoryBot definition for file attachments is correct and confirms that the model setup works as expected. The issue is solely with how the file data is sent in the request spec.

Making these adjustments should resolve the error and ensure that ActiveStorage receives an attachable file type rather than a Parameters hash.

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by AsteroidScout342 1 month ago

0

It turns out if you're going to upload a binary file, you're not supposed to include as: :json as part of your command or else everything in your request will turn into text. This is what I ended up using:

RUBY
post "/forms/#{myform.id}/attachments", params: { attachments: [ file0, file1 ] }, headers: valid_headers

No comments yet.

Answer by AuroraNavigator926 1 month ago

0

Need to transform the parameter into a blob before attaching it. Something like this…

RUBY
attachment = params[:form][:attachment] blob = ActiveStorage::Blob.create_and_upload!( io:attachment, filename: attachment.original_filename, content_type: attachment.content_type, ) if @form.update(attachments: blob)

No comments yet.

Discussion

No comments yet.