Product metafield creation ERROR via Admin GraphQL API — Bad Request 400

Hi everyone,

I’m trying to create a custom metafield definition for products using the Admin GraphQL API, but I keep getting a vague 400 Bad Request response with no detailed error message when calling this from my custom app running in a development store.

The goal is to create a metafield definition for products, and then set metafields.

What I am doing:

Mutation:
mutation CreateMetafieldDefinition($definition: MetafieldDefinitionInput!) {
metafieldDefinitionCreate(definition: $definition) {
createdDefinition {
id
name
}
userErrors {
field
message
code
}
}
}

variables:
{
“definition”: {
“name”: “Custom Field”,
“namespace”: “custom_app”,
“key”: “custom_field”,
“description”: “Test custom field for products”,
“type”: “json”,
“ownerType”: “PRODUCT”
}
}

This worked in the GraphiQL and got a successfull call.
but when calling this mutation in my custom Remix app, I always get this error:

ensureBadgeMetafieldDefinition failed: Response {
status: 400,
statusText: ‘’,
headers: Headers { ‘Content-Type’: ‘application/json; charset=utf-8’ },
body: ReadableStream { locked: false, state: ‘readable’, supportsBYOB: true },
bodyUsed: false,
ok: false,
redirected: false,
type: ‘default’,
url: ‘’
}

Shopify error JSON: {
“errors”: {
“networkStatusCode”: 400,
“message”: “GraphQL Client: Bad Request”,
“response”: {}
}
}

Current scopes:

  • read_products
  • write_products
  • read_themes
  • write_themes

I have tried refreshing app install and scopes multiple times.

Am I missing something?
Could this be an authentication issue?

Any help or insight would be greatly appreciated!
:slight_smile:

A 400 error with no detailed message usually means:

  • The access token is missing or invalid.
  • The request body is not valid JSON or not in the expected format.
  • The endpoint URL is incorrect.

If this is working in GraphiQL but not on your app then it could be an auth issue. If so, some things to check are:

  • Ensure your app is sending a valid Shopify Admin API access token in the X-Shopify-Access-Token header.
  • The request must be a POST to the correct Admin API endpoint:
    https://{shop}.myshopify.com/admin/api/{version}/graphql.json
  • The Content-Type header must be set to application/json.

If you use something like Postman or curl to run the mutation, does it work there?

Thank you for your answer Liam,

This was very helpful.
I was able to confirm that the mutation works using curl. So I ended up changing my implementation to use fetch directly and manually provide the access token and numeric shop ID.
Once I switched to using fetch with the correct X-Shopify-Access-Token, Content-Type: application/json header, and the proper shop GID, everything started working perfectly.

The function shown in the official documentation didn’t work in my case.
So instead of this:
const { admin } = await authenticate.admin(request);
const response = await admin.graphql(
#graphql mutation CreateMetafieldDefinition($definition: MetafieldDefinitionInput!) { metafieldDefinitionCreate(definition: $definition) { createdDefinition { id name } userErrors { field message code } } },
{
variables: {
“definition”: {
“name”: “Ingredients”,
“namespace”: “bakery”,
“key”: “ingredients”,
“description”: “A list of ingredients used to make the product.”,
“type”: “multi_line_text_field”,
“ownerType”: “PRODUCT”
}
},
},
);

const data = await response.json();

I used this:
const shop = session.shop;
const accessToken = session.accessToken;

const query = mutation CreateMetafieldDefinition($definition: MetafieldDefinitionInput!) { metafieldDefinitionCreate(definition: $definition) { createdDefinition { id name } userErrors { field message code } } };

const variables = {
definition: {
name: “Ingredients”,
namespace: “bakery”,
key: “ingredients”,
description: “A list of ingredients used to make the product.”,
type: “multi_line_text_field”,
ownerType: “PRODUCT”,
},
};

const response = await fetch(
https://${shop}/admin/api/2024-04/graphql.json,
{
method: “POST”,
headers: {
“X-Shopify-Access-Token”: accessToken,
“Content-Type”: “application/json”,
},
body: JSON.stringify({ query, variables }),
}
);

and it worked!
Thanks again for your help

Awesome @Selim_Gawad - really glad this worked and I appreciate you posting your solution! Best of luck with the rest of the project and let us know if you hit any other issues :slight_smile: