Intermittent "Something went wrong, please try again" Error in `productSet` Bulk Mutation

Hello Shopify Community,

I am encountering a recurring issue with the productSet bulk mutation using the GraphQL Admin API (version 2024-07, which uses media, and 2024-10, which uses files). Some listings return the following userError:

“Something went wrong, please try again.”

Problem Description:

  1. Intermittent Behavior: The error does not consistently occur. When I retry the request with the same data, it often works without issues.
  2. Generic Error Message: The error message is vague, making it challenging to debug the underlying problem.
  3. Listings Are Not Created: The listings are often not successfully created in Shopify.

Steps to Reproduce:

GET valid data for the next environment variables

const SHOP_DOMAIN = process.env. SHOP_DOMAIN;
const ACCESS_TOKEN = process.env.SHOPIFY_ACCESS_TOKEN;

Run the POC script that reproduces the issue:

// try to associate images at the end:

const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const path = require('path');
const uuidv4 = require('uuid').v4;

// Replace these with your actual Shopify shop domain and access token
const SHOP_DOMAIN = 'vela-test-xavor-large-5-dev.myshopify.com';
const ACCESS_TOKEN = process.env.SHOPIFY_ACCESS_TOKEN;

// Shopify API version
const API_VERSION = '2024-10';

// Shopify GraphQL endpoint
const GRAPHQL_ENDPOINT = `https://${SHOP_DOMAIN}/admin/api/${API_VERSION}/graphql.json`;

// Your provided image data (flattened the array for easier access)
const images = [
  {
    "originalSource": "https://s3.amazonaws.com/dev-images.getvela.com/cb14481cdfb11a02343a1c6f322b200c438fbbc5",
    "contentType": "PRODUCT_IMAGE",
    "alt": "a dog that is laying down with its mouth open"
  },
];

// Your provided product data
const products = [
  // First product data
  {
    "seo": {
      "title": "1Luxurious Cotton Dragon T-Shirt with Elegant Gold Detail  Perfect for Any Occasion",
    "description": "Te presentamos la camiseta DRAGÓN, elaborada en algodón de lujo con detalles dorados. Esta camiseta combina comodidad y estilo, perfecta para cualquier ocasión. Su diseño exclusivo y los lujosos acabados en oro la convierten en una opción única para tu guardarropa."
    },
    "vendor": "large-5-dev",
    "variants": [
      {
        "optionValues": [{ "optionName": "Title", "name": "Default Title" }],
        "inventoryPolicy": "DENY",
        "compareAtPrice": 0,
        "position": 1,
        "taxable": true,
        "price": 1,
        "barcode": null,
        "sku": null
      }
    ],
    "title": "1Luxurious Cotton Dragon T-Shirt with Elegant Gold Detail  Perfect for Any Occasion",
    "tags": ["artistic flair", "casual day outfit", "charm and sophistication", "comfortable clothing", "cotton blend", "dragon t-shirt", "elegant gold detail", "everyday look", "exceptional shirt", "exquisite details", "fashion statement", "gold accents", "luxurious cotton t-shirt", "modern design", "night out attire", "personal style", "polished appearance", "relaxed vibe", "sophisticated fashion", "standout clothing", "stylish apparel", "tailored pants", "timeless elegance", "unique dragon design", "versatile wardrobe staple"],
    "status": "DRAFT",
    "productType": "",
    "productOptions": [{ "name": "Title", "position": 1, "values": [{ "name": "Default Title" }] }],
    "collections": [],
    "descriptionHtml": "Introducing the **Dragon T-Shirt**, crafted from luxurious cotton and embellished with exquisite gold details. This exceptional shirt seamlessly blends comfort and style, making it an ideal addition to your wardrobe for any occasion. The softness of the cotton ensures that you feel as good as you look.The **unique dragon design** adds an artistic flair, ensuring you stand out wherever you go. The subtle gold accents impart sophistication and charm, subtly elevating your everyday look. Whether for a night out or a casual day, this versatile piece effortlessly transitions between different settings. Pair it with your favorite jeans for a relaxed vibe, or dress it up with tailored pants for a more polished appearance.Opt for this distinctive item that not only showcases your personal style but also provides the comfort you desire throughout the day. Don't miss the chance to enhance your collection with the timeless elegance and unique allure of the **Dragon T-Shirt**. Explore its charm today!",
    "handle": `${Date.now()}${Math.floor(Math.random() * 1000000)}`
  },
  {
    "seo": {
      "title": "2Luxurious Cotton Dragon T-Shirt with Elegant Gold Detail  Perfect for Any Occasion",
      "description": "Te presentamos la camiseta DRAGÓN, elaborada en algodón de lujo con detalles dorados. Esta camiseta combina comodidad y estilo, perfecta para cualquier ocasión. Su diseño exclusivo y los lujosos acabados en oro la convierten en una opción única para tu guardarropa."
    },
    "vendor": "large-5-dev",
    "variants": [
      {
        "optionValues": [{ "optionName": "Title", "name": "Default Title" }],
        "inventoryPolicy": "DENY",
        "compareAtPrice": 0,
        "position": 1,
        "taxable": true,
        "price": 1,
        "barcode": null,
        "sku": null
      }
    ],
    "title": "second product",
    "tags": ["artistic flair", "casual day outfit", "charm and sophistication", "comfortable clothing", "cotton blend", "dragon t-shirt", "elegant gold detail", "everyday look", "exceptional shirt", "exquisite details", "fashion statement", "gold accents", "luxurious cotton t-shirt", "modern design", "night out attire", "personal style", "polished appearance", "relaxed vibe", "sophisticated fashion", "standout clothing", "stylish apparel", "tailored pants", "timeless elegance", "unique dragon design", "versatile wardrobe staple"],
    "status": "DRAFT",
    "productType": "",
    "productOptions": [{ "name": "Title", "position": 1, "values": [{ "name": "Default Title" }] }],
    "collections": [],
    "descriptionHtml": "Introducing the **Dragon T-Shirt**, crafted from luxurious cotton and embellished with exquisite gold details. This exceptional shirt seamlessly blends comfort and style, making it an ideal addition to your wardrobe for any occasion. The softness of the cotton ensures that you feel as good as you look.The **unique dragon design** adds an artistic flair, ensuring you stand out wherever you go. The subtle gold accents impart sophistication and charm, subtly elevating your everyday look. Whether for a night out or a casual day, this versatile piece effortlessly transitions between different settings. Pair it with your favorite jeans for a relaxed vibe, or dress it up with tailored pants for a more polished appearance.Opt for this distinctive item that not only showcases your personal style but also provides the comfort you desire throughout the day. Don't miss the chance to enhance your collection with the timeless elegance and unique allure of the **Dragon T-Shirt**. Explore its charm today!",
    "handle": `${Date.now()}${Math.floor(Math.random() * 1000000)}`
  },
  {
    "seo": {
      "title": "11 oz Classic Ceramic Mug - Ideal Gift for Coffee and Tea Lovers",
      "description": "Start your day in style! This 11oz ceramic mug is perfect for coffee, tea, or any of your favorite drinks. It features a comfortable handle and a vibrant, long-lasting design that will brighten every sip. Whether you're using it at home, in the office, or giving it as a gift, it's sure to become your go-to mug. Elevate your morning routine or make someone's day with this classic coffee mug. Looking for the perfect gift for the coffee or tea lover in your life? Look no further than this monogrammed leather Dopp kit. With a generous 255 character limit, you can add a personal touch to this ideal groomsmen gift. This high-quality leather kit is perfect for keeping toiletries organized on trips or at home. Treat yourself or someone special to this elegant and functional gift that is sure to impress. Order now and make any occasion even more special with this monogrammed leather Dopp kit."
    },
    "vendor": "large-5-dev",
    "variants": [
      {
        "optionValues": [{ "optionName": "Title", "name": "Default Title" }],
        "inventoryPolicy": "DENY",
        "compareAtPrice": 0,
        "position": 1,
        "taxable": true,
        "price": 0,
        "barcode": null,
        "sku": null
      }
    ],
    "title": "11 oz Classic Ceramic Mug - Ideal Gift for Coffee and Tea Lovers",
    "tags": ["11 oz mug", "beverage enjoyment", "birthday gift", "ceramic mug", "classic mug", "coffee lovers", "coffee mug", "comfortable handle", "daily ritual", "drinking experience", "durable ceramic", "gift for her", "gift for him", "holiday gift", "home essentials", "kitchen collection", "morning routine", "office mug", "relaxation moments", "stylish drinkware", "tea lovers", "tea mug", "thoughtful gift", "unique gift", "vibrant design"],
    "status": "DRAFT",
    "productType": "",
    "productOptions": [{ "name": "Title", "position": 1, "values": [{ "name": "Default Title" }] }],
    "collections": [],
    "descriptionHtml": "Start your day with style! This **11 oz classic ceramic mug** is perfect for enjoying your favorite coffee, tea, or any other beverage you cherish. Crafted from durable ceramic, this mug features a **comfortable handle** that ensures a secure grip, allowing you to savor each sip with ease. Its **vibrant design** adds a splash of color to your morning routine, making it suitable for both home and office use. Picture yourself taking a moment to unwind, with the steam rising from your drink as you indulge in rich flavors. Each time you fill this mug, it becomes part of your daily ritual.This **classic mug** also makes a thoughtful gift for coffee and tea enthusiasts. Whether for a birthday, holiday, or just to show you care, it’s a lovely way to express affection. With its timeless appeal, this ceramic mug is sure to become a favorite in any kitchen collection. Elevate your drinking experience with our **11 oz classic ceramic mug**—the perfect companion for those precious moments of relaxation and enjoyment. Order yours today and turn every sip into a delightful occasion!",
    "handle": `${Date.now()}${Math.floor(Math.random() * 1000000)}`
  }
];

// Polling function to check if all files are in the READY state
async function checkFilesReady(fileIds) {
  const fileStatusQuery = `
    query fileStatus($ids: [ID!]!) {
      nodes(ids: $ids) {
        ... on File {
          id
          fileStatus
        }
      }
    }
  `;

  const startTime = Date.now();
  const timeout = 60000; // Set timeout to 60 seconds

  while (Date.now() - startTime < timeout) {
    try {
      const response = await axios.post(
        GRAPHQL_ENDPOINT,
        {
          query: fileStatusQuery,
          variables: { ids: fileIds },
        },
        {
          headers: {
            'Content-Type': 'application/json',
            'X-Shopify-Access-Token': ACCESS_TOKEN,
          }
        }
      );

      const files = response.data.data.nodes;
      const notReadyFiles = files.filter(file => file.fileStatus !== 'READY');

      if (notReadyFiles.length === 0) {
        console.log("All files are in READY state.");
        return true;
      } else {
        console.log("Waiting for files to be in READY state...");
        await new Promise(resolve => setTimeout(resolve, 1000));
      }
    } catch (error) {
      console.error("Error checking file statuses:", error);
      return false;
    }
  }

  console.error("Timeout reached while waiting for files to be in READY state.");
  return false;
}

// Mutation for file creation
const FILE_CREATE_MUTATION = `
  mutation fileCreate($files: [FileCreateInput!]!) {
    fileCreate(files: $files) {
      files {
        alt
        createdAt
        id
        fileStatus
      }
    }
  }
`;

// Step 1: Upload images for each product and collect media IDs
async function uploadImagesForProduct(productImages) {
  try {
    const fileInputs = productImages.map(image => ({
      alt: image.alt,
      originalSource: image.originalSource,
      filename: uuidv4(),
    }));

    console.log("Uploading images...", fileInputs);
    const response = await axios.post(
      GRAPHQL_ENDPOINT,
      {
        query: FILE_CREATE_MUTATION,
        variables: { files: fileInputs },
      },
      {
        headers: {
          'Content-Type': 'application/json',
          'X-Shopify-Access-Token': ACCESS_TOKEN,
        }
      }
    );

    const { files, userErrors } = response.data.data.fileCreate;
    console.log("Files created:", files);
    if (userErrors && userErrors.length > 0) {
      console.error("User errors during file creation:", userErrors);
      return null;
    }

    const fileIds = files.map(file => file.id);

    // Poll to ensure all files are in READY state
    const allFilesReady = await checkFilesReady(fileIds);
    if (!allFilesReady) {
      throw new Error("Not all files reached READY state.");
    }

    return fileIds; // Return media IDs for this product
  } catch (error) {
    console.error("Error uploading images:", error);
    return null;
  }
}


// Step 2: Prepare product data with unique media IDs and write to JSONL file
async function prepareProductData() {
  const jsonlPath = path.resolve(__dirname, 'products_upload.jsonl');
  const writeStream = fs.createWriteStream(jsonlPath, { flags: 'w' });

  for (const product of products) {
    // Upload images specific to this product
    const fileIds = await uploadImagesForProduct(images);
    const files = fileIds.map(fileId => ({
      id: fileId,
      contentType: 'IMAGE',
      duplicateResolutionMode: 'RAISE_ERROR'
    }));
    if (!fileIds) throw new Error("Error uploading images for product.");
    console.log("File IDs for product:", fileIds);

    const productEntry = {
      input: {
        ...product,
        files,
        // mediaIds: fileIds,
      }
    };

    writeStream.write(JSON.stringify(productEntry) + '\n');
  }

  writeStream.end();
  console.log(`Products JSONL file created at ${jsonlPath}`);
  return jsonlPath;
}

// Step 3: Stage JSONL file for bulk operation
async function stageJsonlFileForBulkOperation(filePath) {
  const getStagedTargetMutation = (fileName) => `
    mutation {
      stagedUploadsCreate(input: {
        resource: BULK_MUTATION_VARIABLES,
        filename: "${fileName}",
        mimeType: "text/jsonl",
        httpMethod: POST
      }) {

      userErrors {
        field
        message
      },
        stagedTargets {
          url,
        resourceUrl,
          parameters {
            name
            value
          }
        }
      }
    }
  `;

  try {
    // Request upload target
    const query = getStagedTargetMutation(path.basename(filePath));
    const stagedUploadResponse = await axios.post(
      GRAPHQL_ENDPOINT,
      { query },
      { headers: { 'X-Shopify-Access-Token': ACCESS_TOKEN } }
    );

    const stagedTargets = stagedUploadResponse.data?.data?.stagedUploadsCreate?.stagedTargets;
    if (!stagedTargets || stagedTargets.length === 0) {
      throw new Error('Failed to get a valid staged target.');
    }

    const stagedTarget = stagedTargets[0];
    const parameters = stagedTarget.parameters || [];

    // Find the parameter where name is 'key' and get its value
    const stagedUploadPath = parameters.find(param => param.name === 'key')?.value;
    if (!stagedUploadPath) {
      throw new Error('Failed to get staged upload path.');
    }

    // Prepare FormData with required parameters and the file itself
    const form = new FormData();
    parameters.forEach(param => form.append(param.name, param.value));
    form.append('file', fs.createReadStream(filePath));

    // Upload the file to the staged URL
    console.log('form headers:', form.getHeaders());
    await axios.post(stagedTarget.url, form, { headers: form.getHeaders() });
    console.log('JSONL file staged successfully for bulk operation at URL:', stagedTarget.url);

    return stagedUploadPath;  // Return the staged upload path for use in the bulk operation
  } catch (error) {
    console.error('Error staging JSONL file:', error.response ? error.response.data : error.message);
    return null;
  }
}

// Step 4: Start the bulk operation with the staged JSONL URL
async function startBulkOperation(stagedUploadPath) {
  const bulkOperationQuery = `
    mutation {
      bulkOperationRunMutation(
        mutation: """mutation call($input: ProductSetInput!) {
          productSet(input: $input) {
            product {
              id
              legacyResourceId
              title
              handle
              publishedAt
              variants(first: 2000) {
                edges {
                  node {
                    id
                    inventoryItem {
                      id
                      legacyResourceId
                    }
                  }
                }
              }
            }
            userErrors {
              code
              field
              message
            }
          }
        }""",
        stagedUploadPath: "${stagedUploadPath}") {
        bulkOperation {
          id
          url
          status
        }
        userErrors {
          code
          field
          message
        }
      }
    }
  `;

  try {
    const response = await axios.post(GRAPHQL_ENDPOINT, { query: bulkOperationQuery }, { headers: { 'X-Shopify-Access-Token': ACCESS_TOKEN } });
    const { bulkOperation, userErrors } = response.data.data.bulkOperationRunMutation;

    if (userErrors && userErrors.length > 0) {
      console.error("User errors during bulk operation initiation:", userErrors);
      return null;
    }

    console.log('Bulk operation initiated with ID:', bulkOperation.id);
    return bulkOperation.id;
  } catch (error) {
    console.error('Error initiating bulk operation:', error.response ? error.response.data : error.message);
    return null;
  }
}

// Step 5: Monitor the bulk operation status
async function monitorBulkOperation() {
  return new Promise((resolve, reject) => {
    const interval = setInterval(async () => {
      try {
        const query = `
          {
            currentBulkOperation (type: MUTATION) {
              id
              status
              errorCode
              createdAt
              completedAt
              objectCount
              fileSize
              url
            }
          }
        `;
        const response = await axios.post(GRAPHQL_ENDPOINT, { query }, { headers: { 'X-Shopify-Access-Token': ACCESS_TOKEN } });
        console.log('Checking bulk operation status...');
        const operation = response.data.data.currentBulkOperation;

        console.log(`Bulk operation status: ${operation.status}`);

        if (operation.status === 'COMPLETED') {
          clearInterval(interval);
          console.log('Bulk operation completed successfully.');
          console.log('Results URL:', operation.url);
          resolve(operation);
        } else if (operation.status === 'FAILED') {
          clearInterval(interval);
          console.error('Bulk operation failed:', operation.errorCode);
          reject(new Error(`Bulk operation failed with error code: ${operation.errorCode}`));
        }
      } catch (error) {
        clearInterval(interval);
        reject(error);
      }
    }, 1000);
  });
}

// Main function to execute the steps sequentially
(async () => {
  try {
    const jsonlFilePath = await prepareProductData();
    for (const product of products) {
      // Upload images specific to this product
      const fileIds = await uploadImagesForProduct(images);
      console.log('fileIds:', JSON.stringify(fileIds.map(fileId => ({ id: fileId}))));
    }
    const stagedUploadPath = await stageJsonlFileForBulkOperation(jsonlFilePath);

    if (stagedUploadPath) {
      const bulkOperationId = await startBulkOperation(stagedUploadPath);
      await monitorBulkOperation(bulkOperationId);
    }
  } catch (error) {
    console.error('Error in bulk product upload:', error);
  }
})();

  • I upload images for products using the fileCreate mutation.
  • I include images in the bulk mutation via productSet once images are ready.

Expected Behavior:

The bulk mutation should process all items without returning generic errors when no issues exist with the input data.

Observed Behavior:

  • Some listings return the error: "Something went wrong, please try again."
  • Listings that “failed” aren’t created successfully in the Shopify admin.

Questions:

  1. Is this error related to a known issue with the productSet bulk mutation?
  2. Are additional debugging tools or logs available to better understand why this happens?
  3. Has anyone else observed this behavior with similar mutations?

Additional Information:

  • GraphQL API Version: 2024-10 (files) and 2024-07 (mediaIds)
  • Shopify Shop Domain: vela-test-xavor-large-5-dev.myshopify.com

Any insights or recommendations would be greatly appreciated!

Thanks in advance,
Giovanni

@Liam-Shopify do u mind taking a look please?

:wave: @yovasx2
Thanks for reaching out.
Would you mind sharing a request id of one of the failed calls.
Thanks

Hello Shopify Community,

@Asaf_Gitai asked about detailed request IDs for the intermittent issue in the productSet bulk mutation. Below, I provide a complete breakdown of the operation, including all relevant request IDs at each step of the process. I hope this information helps pinpoint the root cause of the issue.


Detailed Breakdown with Request IDs

  1. Uploading First Set of Images:

    • Request ID: ef26ec1d-69b1-4ef3-814d-29b4e65b4ff0-1732126155
    • File Readiness:
      • First Check: b5b8c0f8-ec97-4f07-8543-b66b1213b1bd-1732126156
      • Second Check: e304488e-8a6b-458a-9d2b-5c68bed312c0-1732126157
      • Final Check: bf867640-ade6-4b45-bc3a-9ec78d010da3-1732126158
      • Status: All files reached the READY state.
    • File IDs:
      [
        'gid://shopify/MediaImage/26408897282136',
        'gid://shopify/MediaImage/26408897314904',
        'gid://shopify/MediaImage/26408897347672',
        'gid://shopify/MediaImage/26408897380440',
        'gid://shopify/MediaImage/26408897413208',
        'gid://shopify/MediaImage/26408897445976',
        'gid://shopify/MediaImage/26408897478744',
        'gid://shopify/MediaImage/26408897511512'
      ]
      
  2. Uploading Second Set of Images:

    • Request ID: 77b320bf-7622-4b03-b58b-01e41bcc1daf-1732126158
    • File Readiness:
      • First Check: ef012aa0-6360-4fcc-848b-c13b02fea2ea-1732126159
      • Second Check: fde62dad-7fcf-4aaf-811a-46fcdff8361c-1732126160
      • Final Check: 9b86b2b6-8ab5-4c80-bfa6-efc62cf1ab28-1732126162
      • Status: All files reached the READY state.
    • File IDs:
      [
        'gid://shopify/MediaImage/26408897937496',
        'gid://shopify/MediaImage/26408897970264',
        'gid://shopify/MediaImage/26408898003032',
        'gid://shopify/MediaImage/26408898035800',
        'gid://shopify/MediaImage/26408898068568',
        'gid://shopify/MediaImage/26408898101336',
        'gid://shopify/MediaImage/26408898134104',
        'gid://shopify/MediaImage/26408898166872'
      ]
      
  3. Staging JSONL File:

    • Request ID: e81b69e6-1956-4e41-b4f1-997a66f1a4b9-1732126162
    • JSONL Staging URL: https://shopify-staged-uploads.storage.googleapis.com/
  4. Bulk Operation Initiation:

    • Request ID: 8fa5fe3f-8f93-4a85-920c-9d8ab245c9f5-1732126162
    • Bulk Operation ID: gid://shopify/BulkOperation/3512889671768
  5. Bulk Operation Status Monitoring:

    • Request ID (First Check): e9351d07-9dac-442c-afb9-cd9cb5926a40-1732126164
    • Request ID (Final Check): 6ae9d656-9578-4e87-b3d2-b6443ecdd424-1732126165
    • Status: COMPLETED.
  6. Results:


Observations

  • Each process step is tracked with unique request IDs for traceability.
  • File readiness checks ensured all files were in the READY state before proceeding.
  • The bulk operation was completed successfully, and results were available for download.

Questions for Shopify Community/Support

  1. Could these request IDs help diagnose backend issues causing intermittent failures?
  2. Are there specific patterns or conditions that might lead to the "Something went wrong" error?
  3. Would enabling additional debugging or logging improve our ability to identify and resolve this problem?

Any additional insights would be greatly appreciated!

Thank you,
Giovanni

We have been experiencing the exact same issue. @yovasx2 On behalf of all of our us developers, thank you for extensively documenting the sequence!