bulkOperationRunMutation gives Unexpected file structure - expected JSONL

Topic summary

Main issue: bulkOperationRunMutation returned a user error “Unexpected file structure - expected JSONL” when attempting a Shopify bulk import.

Context: The staged upload to Shopify succeeded (XML PostResponse with Location, Bucket, Key), but the subsequent GraphQL mutation failed. The workflow followed Shopify’s docs for bulk operations imports.

Root cause: The multipart form data’s file field was set to a file path string instead of the file’s binary contents.

Resolution: Open the JSONL file in binary mode and pass the file handle in the multipart form (e.g., open(‘/path/products.jsonl’, ‘rb’)). After this change, the import proceeded correctly.

Clarification: JSONL (JSON Lines) is newline-delimited JSON objects. Shopify’s bulk import expects this exact structure in the uploaded file.

Helpful tip: Use curlconverter.com to translate a curl --form POST (e.g., to shopify-staged-uploads.storage.googleapis.com with key=‘…’) into language-specific code, ensuring proper file upload semantics (i.e., sending file bytes, not just a path).

Status: Resolved; no further action items noted.

Summarized with AI on January 25. AI used: gpt-5.

I have been following the documentation here: https://shopify.dev/api/usage/bulk-operations/imports#generate-the-uploaded-url-and-parameters

Yet when I run bulkOperationRunMutation to run the import I get

{u’bulkOperationRunMutation’: {u’bulkOperation’: None, u’userErrors’: [{u’field’: None, u’message’: u’Unexpected file structure - expected JSONL’}]}}

I used the example file

I use this to add the file and it is successfully added

r = requests.post(self.endpoint, files=self.multipart_form_data, headers={
‘X-Shopify-Access-Token’: self.shopify_access_token
})

I get a response like this

<?xml version="1.0" encoding="UTF-8"?>

https://shopify.s3.amazonaws.com/tmpSTUFFSTUFFshopifySTUFFSTUFF"

Then this to run it

r = requests.post(self.endpoint, json={‘query’: query}, headers={
‘X-Shopify-Access-Token’: self.shopify_access_token,
‘X-GraphQL-Cost-Include-Fields’: self.shopify_include_cost_fields
})

I am using all the data from the example, what am I missing?

Thanks

Grant

I think have this sorted

I was using this

self.multipart_form_data[“file”] = ‘/servicecatalogue/development/import/products.jsonl’

But should be this I think

self.multipart_form_data[“file”] = open(‘/servicecatalogue/development/import/products.jsonl’, ‘rb’)

Thanks

Grant

2 Likes

Thx you saved my life, with this tool : https://curlconverter.com/

you can see what is the alternative in your languag. If I type

curl --location --request POST ‘https://shopify-staged-uploads.storage.googleapis.com/
–form ‘key=“tmp/21759409/bulk/2d278b12-d153-4667-a05c-a5d8181623de/bulk_op_vars”’ \

and choose language to see how to replace the open method