example-app Errors 

Each error returned by the example-app API has a specific error code string starting with GGT_ that identifies the exact error that caused the API call to fail. These codes are intended to be searchable -- you should find information on them on this page, but you can Google them too. Error codes are also helpful to Gadget support engineers when trying to help you correct an error, so please include them when communicating with Gadget staff if possible!


A request was made to act on a model that provided invalid data for that model. Validity is determined by the model's validations as configured in the Editor, so this can happen if you make a call without required data, or pass data that doesn't pass a configured RegExp validation, or data that doesn't pass a custom code validation for example. More information can be found in the Action Result Format section of the documentation for the model action you are trying to invoke.

The invalid fields and the validation error messages suitable for showing to a user can be determined by looking at the validationErrors field of the GraphQL response, or the validationErrors of the InvalidRecordError Error object thrown by the generated JavaScript client.

1import { InvalidRecordError } from "@gadgetinc/api-client-core";
2import { api } from "../my-api-setup";
3try {
4 await api.exampleModel.create({ name: undefined }); // try to create an example model without a required field
5} catch (error) {
6 if (error instanceof InvalidRecordError) {
7 // `error.validationErrors` is an array of { apiIdentifier: string, message: string } objects describing which fields of the model were invalid
8 console.warn(error.validationErrors); // outputs => [ { apiIdentifier: "name", message: "is required" } ]
9 }
1mutation {
2 createExampleModel(exampleModel: { name: null }) {
3 success # will be false
4 errors {
5 # all errors have a code and a message
6 code
7 message
8 # we can look at the typename of the error to know which class of error it was specifically
9 __typename
10 # Get the validation errors on the InvalidRecordError type, if it is one. There can be other error types, so not all errors will have the `validationErrors` property. Gadget uses a GraphQL interface here to allow asking for these properties where needed.
11 ... on InvalidRecordError {
12 validationErrors {
13 apiIdentifier
14 message
15 }
16 # other fields available on the invalid record error
17 record # the record which failed validation
18 model {
19 apiIdentifier # the details of the model this error was thrown for
20 }
21 }
22 }
23 }


Thrown when the server encounters data that is not unique despite the existence of unique validation on a field. If encountered, it is likely that you added a unique validation to a field that has duplicate data and the validation is in an errored state.


While logging in Gadget encountered an error while saving the user on the newly authenticated session. If encountered, double check the user has the correct permissions to view or manipulate data in example-app.


A request was made to an application that successfully authenticated, but Gadget was unable to store the authenticated user's information because there was no session to store it on. This happens when authentication attempts are made using a request to the Gadget API that doesn't send cookies, or when using API keys for authentication against the example-app API.

If you're using API Key authentication (see the Authentication docs, you shouldn't (and can't) login those clients. API Key authenticated clients prove their identity by passing the API key itself, and get their roles right from the API Key as opposed to any one user. Currently, Gadget does not support API Key based authentication that acts as or masquerades as a particular user.

If you're using Browser Session Authentication (see the Authentication docs), you must ensure that your GraphQL client is storing and sending cookies as you make requests. If you're using fetch in a web browser for example, you need to ensure you pass the credentials: "include" option to the fetch call to include the cookies that power Session Cookie authentication. For more information on the options that fetch takes, see the MDN Fetch docs.


Too much time was taken to execute an action. Actions and Global Actions are given 3 minutes to execute all effects. See the Actions guide for more details.


An invalid action was taken on a record, and there could be no transition to the model's state. This is most likely because the action you are trying to take does not exist, check for typos.


Gadget tried to read a record from the database whose stored data is no longer valid according to the current schema for the model. For example, a record was written with null for a given field, and then a Required validation was added to that field, rendering the record invalid. Gadget can't read the record and return data that doesn't conform to the schema that's been configured, so this error is thrown.

This can happen for a few reasons:

  • a new field was added to a model and marked as required before being set for existing records
  • new validations were added to an existing field, and records had already been saved with that field
  • a field's type was changed and then marked as required without data being set for existing records

To fix this error, you can either remove the offending validation from the model's schema (usually, make the field no longer required until all the records have a value set), or you can remove the offending data from the database.

Note: Records that throw this error when accessed via the high level GraphQL API can still be accessed via the Internal API for your application. The Internal API won't throw this error when reading records.


An API request failed to authenticate via the API Key method because an incorrect or invalid API key was passed in the Authorization header. This can be because the API Key was deleted, was misspelled, or was improperly passed in the HTTP Authorization header. Consult the Authentication docs for more information on authenticating via an API Key.


Gadget supports full ACID transactions via its GraphQL over WebSockets API, or using the api.connection.transaction function. GGT_INCORRECT_TRANSACTION_SEQUENCE errors are returned when the startTransaction, rollbackTransaction, or commitTransaction mutations are called in the incorrect order, or outside a WebSockets connection to the Gadget API. If you're using the generated JS client for example-app, the api.connection.transaction handles the correct ordering of each transaction calls automatically, so we recommend using it where possible.

There's a few different common patterns that can cause this issue:

  • calling the transaction fields via a normal GraphQL-over-HTTP request. Gadget doesn't support this -- you need to invoke these fields via a WebSocket connection.
  • calling the transaction fields out of order. To process a transaction, you must startTransaction before doing any mutations, and then you must either commitTransaction or rollbackTransaction to complete the transaction. Closing the websocket connection automatically rolls back the open transaction
  • accidentally running more than one transaction at once. This can happen if you're using the generated Gadget client and opening .transaction functions in async or parallel code. You can't use the same WebSockets connection to process simultaneous transactions. You can open multiple WebSocket connections, or run your transactions one at a time to avoid this.


Gadget supports full ACID transactions via it's GraphQL over WebSockets API and has a limit for each transaction's duration of 5 seconds. Gadget wraps all run effects in a transaction, so all of them must complete within this 5 second window to avoid timing out and throwing this error. If you'd like to do something long running, like making an external HTTP call, it's best to do that as a success effect so that the important part of the work is completed and committed first.

There's many things that can result in slow effects, like:

  • making HTTP calls within run effects on an action
  • fetching or trying to mutate too many related records in an effect
  • uploading files or streaming data to and from the disk or an external service
  • doing any very demanding computations like evaluating an ML model

To avoid this issue, ensure your effects complete quickly.


Gadget applications are backed by a scalable Postgres database that can handle thousands of concurrent operations and transactions. When under load, or when asked to perform a particularly slow query, the backend database can take too long to execute it and threaten the health of the application. Gadget won't allow any individual query to run for longer than 3 seconds. Queries that take too long are aborted and this error is thrown. This can affect both reads and writes.

There's many things that can result in slow queries, like:

  • fetching many relationships of a model at the same time
  • fetching many records for a model at the same time
  • having too many rows of data stored for the same model.

To try to make your queries fit within the timeout, you can adjust a few things:

  • fetch fewer relationships at a time, or fewer relationships of relationships
  • fetch a smaller page size (with the first and last arguments to a GraphQL call)
  • split up large tables into smaller ones

Query performance optimization can be a challenging task, so if you're stuck and would like some help, feel free to join us in the Gadget Discord to talk through your specific issue!


This error is thrown when API calls try to query or mutate records by ID that don't exist. If you try to invoke an action on a record ID that no longer exists (or never existed) in the database, you'll see this error code. It can also arise from trying to find a record by a field value (using the .findByX field finders in the generated API client) where no record exists for that field value.

Avoid this error by ensuring you don't try to act on deleted records, or checking if a record exists by querying for it before trying to act on it.

Finders can be adjusted to use .findByX finders if you expect the record to exist, or findMany({filter: { field: "some-value"}}) finds if the record may not exist.


This error is thrown when an API call tries to execute validations configured on a model, and one of those validations is not yet configured, or configured incorrectly. API calls to this model can't be processed until the validation is correctly configured, which can be done in the Gadget Editor for the model in question.

Common causes of this error code are:

  • a string length or number range validation without a minimum or a maximum set
  • an invalid RegExp pattern for a RegExp validation

The specific error message for this error should refer to which type of validation is causing the issue, and which model field the misconfigured validation is on.


This error is thrown when an API call is made that invokes something the caller doesn't have permission to do. Resources in Gadget are all protected by an access control system that only allows callers to read or write data they have been granted permission to access via their roles. GGT_PERMISSION_DENIED means the caller does not have a role which can access some bit of accessed data.

GGT_PERMISSION_DENIED errors don't return a lot of data to the caller to prevent leaking information about the system's access control system or helping attackers gain access, but, they do log details about why permission was denied to the application's logs. If you're encountering this error and can access the Gadget application's internal logs, you can see the following error properties:

  • actorRoleKeys: which role keys the actor has been granted. Role keys are set differently for different actors in the system, see the Access Control guide for more information.
  • resource: which resource the request was trying to access but denied access to. The resource can be a:
    • model record read, which will look like resource: { model: "widget", read: true }
    • actions on models, which will look like resource: { model: "widget", action: "create" }
    • global actions, which will look like resource: { globalAction: "syncShopify" }
  • actor: what the request actor was determined to be. The actor for a request can be:
    • an API key, which will look like actor: { apiKey: "gsk-some-api-key" }
    • a logged in user or session within your application , which will look like actor: { model: "user", { id: 123 } or actor: { model: "session", record: { id: 123 } }
    • a logged in developer working on the application, which will look like actor: { developer: true, id: 123, email: "[email protected]" }
    • the Gadget platform itself running calls, which will look like actor: { platform: true }.


This error is thrown by the Gadget platform when something unexpected goes wrong within the platform itself during the processing of your request. This error is the platform's fault, and generally can't be actioned by developers. We realize this error can be frustrating and we take great care to avoid the root causes of it by keeping our infrastructure in tip top shape. If this error is plaguing you, please reach out to us and we can work with you to correct the root cause.


This error is thrown by the Gadget platform when something unexpected goes wrong when executing your code. If an error is thrown during any custom code, Gadget is unable to discern exactly why the error happened, so it produces this code. This error code can come from custom code effects or custom code validations.


This error is thrown when a field is set up incorrectly inside Gadget. Different fields require different bits of data in order to function properly, so if you are getting this error when trying to run GraphQL queries, ensure the field doesn't have any problems associated with it.

Most often, this error is thrown for misconfigured relationship type fields, like belongs to, has many, has many through or has one when the are missing key information. All relationships need to have the related model set up correctly, so if that has never been set, or if the related model has been deleted, the relationship field can't be accessed.

To configure a relationship, you must select the related model, and fill out any other required information in the relationship configuration form. GraphQL schema generation cannot continue until the relationship has been configured correctly.


This error is thrown when a Gadget requires an action to be present but cannot find it. This often happens when Gadget is asked to perform a high level task that requires some actions to be run, like a _converge nested action invocation or processing a webhook payload from a third party service. By default, models have a create, update, and delete action that Gadget can use to implement these high level tasks, but if you have deleted one of those actions, Gadget can't perform the high level task. To remedy this, restore the default actions that the model might need, or avoid triggering the high level task.

For models provided by a Connection, removing models and then reinstalling the connection will reset changed models back to their original state, restoring any deleted actions.


This error is thrown when a Gadget is asked to perform a high level task that requires some fields to be present in a model's schema, like syncing a connection, but the fields are missing. This can happen if you've deleted a field from a model, or if a connection has been upgraded and requires new fields to exist. To remedy this, restore any deleted fields to models mentioned in the error message.

For models provided by a Connection, removing models and then reinstalling the connection will reset changed models back to their original state, restoring any deleted fields.


This error is thrown when a Gadget is asked to perform a high level task that requires some fields to be present on a record, but the fields are missing. This can happen if you've created or updated records using the Internal API that Gadget expects to be a certain way.

For records that are missing data, if you have the data, you can fix them by making API calls to update the records, or you can delete the erroneous records if you no longer need them.


An invalid argument value was given to some field in the query. For example, the in filter for model queries must not be an empty array.


An invalid action name was given to the example-app API. For example, a nested action was provided that doesn't actually correspond to an action in the application. The client making the request will have to be updated to ensure they reference an action that exists.


An unknown type was encountered while processing the input params schema from one of your custom code effects. You should find the file specified in the error message and see if there's a typo or unsupported type in the input params schema that is described by export const params. Gadget currently supports simple object and scalar types from JSON Schema.


A request was sent to Gadget that needed to be authenticated but there was no session to authenticate in scope. This is most likely due to a user querying for the current session while using an API key as their authentication method. If access to the currentSession is needed for authentication purposes, switch the authentication mode to use cookies instead. Find more information in the Authentication guide.


A request was sent to Gadget that tried to authenticate, but the request specified an authentication mode which Gadget doesn't recognize. The valid authentication mode strings are browser-session, api-key and internal-auth-token.


This error is thrown when a code snippet is either missing a configured source file or the source file is pointing to an invalid file path. This can be remedied by ensuring that the code effect or validation in question has a valid code snippet path selected in the Gadget Editor.


This error is thrown when the database objects that Gadget uses to store your model, like tables and columns, have yet to be set up.

This error should only happen in development environments, and should resolve itself in a short amount of time. If the error doesn't go away, contact Gadget support.


A json field has a default value configured that isn't a valid JSON string. The developer must provide a valid JSON for the default string so Gadget can apply it to new records.


This error is thrown when we receive 401 Unauthorized when accessing Shopify on your behalf. This can happen when we sync a shop that has uninstalled your app. To remedy this, you can click the "Mark As Uninstalled" button next to the affected shop on the shop installs page.


This error is thrown when the Shopify session token can't be validated. This can happen when your application's versions of @shopify/app-bridge-react and @gadgetinc/react-shopify-app-bridge are incompatible. To fix this, update your application's dependencies to the latest versions.

You can update the Gadget package in 2 ways:

  • Using the command palette's Update Gadget-provided npm packages command
  • Running yarn install @gadgetinc/react-shopify-app-bridge@latest on the command palette or in your terminal

If you're not yet on @shopify/app-bridge-react v4, take a look at these upgrade steps.


Apps built in JavaScript use the standard package.json file to manage dependencies installed from npm, as well as the packages Gadget generates like the API client. If the package.json file is not valid JSON, your application can't run, and Gadget can't edit the file. Fix this error by correcting the JSON parse error in package.json.


In order to protect the Gadget platform, each Gadget application has a limited capacity for executing actions or serving an application's HTTP routes. This error is returned if there are more than 300 concurrent such requests. API clients receiving this error can retry the request after a short time to wait for more backend resources to be available.


Right after they're created, Gadget applications finish setting themselves up in the background by installing dependencies and bootstrapping all the required infrastructure under the hood. This error is thrown when a request is made to a Gadget application before this background work has been completed as the application is not yet ready to serve requests. To avoid this error, wait until the application has finished setting up. Background application setup generally takes no longer than 30s.


This error is thrown when the store in question has an unpaid due(s). According to the Shopify spec this 'frozen' state goes into effect after the bill's due date. Some reasons as to why the payment may have failed on Shopify is listed in this Shopify Spec.


This error is thrown when the Gadget app is unable to communicate with your Shopify store. Typically in cases like these the recommended steps to fix are...

  1. Attempt to contact the store owner.
  2. Uninstall & Reinstall the app on Gadget (potentially force uninstall if the Shopify store no longer exists or the owner is unreachable).
  3. Contact Shopify.


This error is thrown when Shopify returns a 403 from an API request. Often, this is related to your app missing permissions for protected customer data.


There are two reasons why you might see this error:

  1. A request to Shopify responded with HTTP Status 429 and the request will not be retried.

    Shopify clients created by Gadget are configured to retry 429 errors 6 times. After 6 attempts, the client will throw an error with this error code. If you are seeing this error frequently, it means you are sending too many requests to Shopify in a short period of time such that even with 6 retries, some are still failing.

  2. You are sending 100+ concurrent requests to Shopify from a single shopify client.

    Gadget's Shopify clients will throw this error if you start more than 100 concurrent requests. Shopify never supports more than this many requests to the same shop simultaneously, and will apply aggressive DDoS protections that can completely break your application if you sustain this request rate.

    A common symptom of this is performing a Promise.all/Array.map over a large number of records and making a request for each:

    1const products = await getABunchOfProducts();
    2const shopify = connections.shopify.current;
    4// bad -- this will send `products.length` requests to Shopify at the same time
    5products.map(async (product) => {
    6 await shopify.product.update(product.id, { foo: "bar" });
    9// good -- this will send `products.length` requests to Shopify one at a time
    10for (const product of products) {
    11 await shopify.product.update(product.id, { foo: "bar" });

    Visit our building shopify apps guide for more information on how to manage Shopify API rate limits.


This error is thrown when there is a problem communicating with the Shopify API that isn't captured by the other GGT_SHOPIFY error codes. The most common causes are making requests to frozen or cancelled Shopify shops, or 500 or 502 errors caused by bugs within Shopify.

These errors are best investigated by examining your logs in Gadget for any relevant or more detailed information about the error.


This error is thrown when two vectors are stored in Gadget that have different dimensions. If you create a record with a vector with 3 dimensions, and a second record with 4 dimensions, these two vectors can't be compared to sort or filter.

To fix this error, you can:

  • delete the records with the incorrect dimensions
  • split up your vector field into different fields altogether, each with vectors of exactly one dimension

This error can also occur if you pass an input vector to the vector comparison functions (like cosineSimilarityTo) that has a different dimensionality than the vector field you're comparing it to. To fix this, ensure you pass an input vector with the same number of dimensions as the stored vectors.


This error is thrown when the save method in the create action throws an exception and that exception is explictly caught in such a way that it does not bubble up - the error is suppressed. To avoid this error do not suppress errors from the save function in the create action.


This error is thrown by Gadget on a sync to a connected Shop model containing a cancelled, frozen, or fraudulent value within the plan_name field. This means that the shop connected to Gadget is likely on an expired Shopify plan.


This error is thrown when the platform receives an error from the database for your app. It is recommended to retry the your entire Gadget action where and when you are certain there will be no unexpected side effects.


This error is thrown when the generated API client is instantiated with an API Key in the browser. API keys are secret passwords that grant priviledged access to your backend Gadget data. If they are recorded in your frontend JavaScript assets, anyone with access to your frontend code can read the value of the key, and make requests using it. Attackers can use this to spoof acccess to your application, so you must not include your Gadget API Keys in any frontend JavaScript.

To avoid this error, you should use a authentication method safe for client-side use in your application. There are a couple options:

  • if client-side users need to log in and out, Gadget has a built-in authentication system for your application that uses cookies and localStorage tokens to persist user identity. To use it instead of an API key, pass { authenticationMode: { browserSession: true } } to your client constructor:
import { Client } from "@gadget-client/my-app";
// use cookies or localStorage for authenticating client side users
export const api = new Client({ authenticationMode: { browserSession: true } });
  • if client-side users don't need to log in or out, you can use the AuthenticationMode.Anonymous authentication mode, which doesn't require an API key. Each API call will use the unauthenticated role when accessing data, so they can only access model data and actions you've granted permission for in the Roles & Permissions screen.
import { Client } from "@gadget-client/my-app";
// don't authenticate client side users, use the `unauthenticated` role
export const api = new Client({ authenticationMode: { anonymous: true } });

See the Access Control guide for more information.

If you're making calls from a server-side context like a node.js app or another Gadget app, API Keys should work fine.

If you need to override this error and you are certain that it is secure to expose your API Key to the browser, you can pass dangerouslyAllowBrowserApiKey: true with your authentication options to suppress the error:

import { Client } from "@gadget-client/my-app";
// WARNING -- this is almost always insecure, and should be avoided! hackers can read your API key!
export const api = new Client({
authenticationMode: { apiKey: "gsk-***", dangerouslyAllowBrowserApiKey: true },


This error is thrown when a background action id is associated with multiple background actions within the same environment. This means you likely have 2 or more background actions with the same id.

To resolve this you must assign each background action a unique id. id's cannot be reused once assigned to a completed or failed action.