gatsbyjs/gatsby · error · OperationError
Operation ${id} failed with ${errorCode}
Error message
Operation ${id} failed with ${errorCode} What it means
Thrown by completedOperation when a Shopify bulk operation reaches a failed terminal status (`FAILED` or `CANCELED`). The OperationError carries the full bulk operation node (error.node) so callers can read errorCode, id, and status. Common errorCodes include ACCESS_DENIED, TIMEOUT, and schema validation failures. The poll loop checks status on an interval and exits as soon as a failed status is observed.
Source
Thrown at packages/gatsby-source-shopify/src/create-operations.ts:188
* surface feedback to the user suggesting that they increase
* the interval.
*/
async function completedOperation(
operationId: string,
interval = 1000
): Promise<{ node: IBulkOperationNode }> {
let operation = await graphqlClient.request<{
node: IBulkOperationNode
}>(OPERATION_BY_ID, {
id: operationId,
})
let waitForOperation = true
while (waitForOperation) {
if (failedStatuses.includes(operation.node.status)) {
waitForOperation = false
throw new OperationError(operation.node)
}
if (operation.node.status === `COMPLETED`) {
waitForOperation = false
return operation
}
await new Promise(resolve => setTimeout(resolve, interval))
operation = await graphqlClient.request<{
node: IBulkOperationNode
}>(OPERATION_BY_ID, {
id: operationId,
})
}
throw new Error(`It should never reach this error`)
}View on GitHub (pinned to 8b06340921)
Solutions
- Inspect error.node.errorCode — for ACCESS_DENIED, update the Shopify app's access scopes to include the required read permissions.
- Re-run the failing bulk query manually against the Shopify Admin API to capture the precise error message.
- Confirm the apiVersion in plugin options matches the schema the query targets.
- For TIMEOUT/CANCELED on very large stores, narrow the query (e.g. filter by updated_at) to reduce operation size.
Example fix
null
Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
function isOperationError(e): e is Error & { node: { errorCode: string; id: string; status: string } } { return e instanceof Error && 'node' in e && typeof (e as any).node?.errorCode === 'string' } Try / catch
try { await completedOperation(opId) } catch (e) { if (isOperationError(e)) { if (e.node.errorCode === 'ACCESS_DENIED') reporter.panic('Shopify app missing required access scopes'); else reporter.warn(`Bulk op ${e.node.id} failed (${e.node.errorCode}); retrying`) } throw e } Prevention
- Keep the Shopify app's access scopes aligned with the fields queried
- Pin apiVersion and review release notes before upgrading
- Run bulk operations during low-traffic windows to reduce TIMEOUT risk
- Log errorCode frequencies to detect scope drift early
When it happens
Trigger: Bulk query references a field the app's access scopes do not permit (ACCESS_DENIED); malformed bulk query (syntax/schema error); operation timed out on Shopify's side; operation manually canceled; Shopify rate-limited the bulk operation creation.
Common situations: App access scopes changed and no longer include products/orders read; query was edited to reference a field the API key cannot access; large store hitting Shopify bulk operation limits; apiVersion mismatch between schema and query.
Related errors
- Source GraphQL API: HTTP error ${response.status} ${response
- response.statusText
- BabelPluginRemoveGraphQLQueries: String interpolations are n
- BabelPluginRemoveGraphQLQueries: Unexpected empty graphql ta
- BabelPluginRemoveGraphQLQueries: GraphQL syntax error in que
AI-assisted analysis of gatsbyjs/gatsby@8b06340921 (2026-08-13).
Data as JSON: /api/errors/70a3c9c70920016b.
Report an issue: GitHub.