GraphQL can return both data and errors in the same response for queries or mutation operations that result in partial success. For example, the record operation succeeds, but some return fields aren’t accessible due to the user’s permission.
Follow these error-handling strategies.
Strict: Treat Any Errors as Failure
This strategy ensures a complete failure if any GraphQL errors are present in the response, regardless of whether partial data was also returned. As the most conservative approach, it guarantees that the app processes data only when the request was entirely successful. Use this strategy with queries where incomplete data is misleading.
1if(result?.errors?.length){2 throw new Error(result.errors.map((e)=> e.message).join("; "));3}4const data = result?.data;
Tolerant: Log Errors but Use Partial Data
Data SDK preserves partial data from GraphQL error responses. When the server returns both data and errors, both fields are available in the result. This strategy allows you to gracefully degrade the UI by rendering the fetched data while logging errors for fields that failed.
Partial success responses where both data and errors are present aren’t cached. Each subsequent query re-fetches data from the network.
Note
This strategy is suitable for cases where partial data is still usable by the app, even if the GraphQL request returns errors. The errors are logged for review, but the app continues to process the available data. Use this strategy when partial data is still useful and the UI can degrade gracefully, such as with an error on an optional field.
1const result =2(await dataSdk.graphql?.query)<3 AccountData>4{5 query: GET_ACCOUNT,6 variables: { id: accountId},7};89if(result?.errors?.length){10 console.warn("GraphQL partial errors:", result.errors);11}1213// result.data may contain partial data even when errors are present14const data = result?.data;
Fail Only When No Data is Returned
This is the least restrictive strategy. It permits the app to use any data that was successfully retrieved, even if partial errors occurred during the GraphQL request. The app throws an error only and fails if the response contains errors and no usable data was returned. For example, you can use this strategy in mutations where the operations succeed but some return fields aren’t accessible.
1// Fail only when no data is returned2if(result?.errors?.length && !result?.data){3 throw new Error(result.errors.map((e)=> e.message).join("; "));4}5if(result?.errors?.length){6 console.warn("Partial success with errors:", result.errors);7}8const data = result?.data;
Network and Runtime Transport Errors
When handling fetch() errors for the Data SDK, consider transport and runtime errors where no responses are returned. For example, transport errors can include network failures, aborted requests, or runtime exceptions.
When handling fetch() errors for the Data SDK, you can run into HTTP app errors with a non-2XX response that includes an error payload. For auth-sensitive flows, consider handling 401 and 403 errors with dedicated user actions, such as a sign-in refresh or a permission guidance message.
1const dataSdk = await createDataSDK();2const response = await dataSdk.fetch?.("/services/apexrest/auth/login", {3 method: "POST",4 headers:{"Content-Type": "application/json", Accept: "application/json"},5 body: JSON.stringify(payload),6});78if(response.status === 401){9 throw new Error("Authentication required or session expired.");10}11if(response.status === 403){12 throw new Error("You do not have permission to perform this action.");13}14if(!response.ok){15 throw new Error(`Request failed: ${response.status} ${response.statusText}`);16}
Example: Partial Success in Update
This example shows how to handle partial success in an update operation.
1async function updateAccount(id: string, updates: Partial<Account>){2 const dataSdk = await createDataSDK();34 const result = await dataSdk.graphql?.mutate<UpdateAccountMutation>({5 mutation: UPDATE_ACCOUNT,6 variables:{7 input:{8 Id: id,9 Account: updates,10},11},12});1314 // Check if the mutation succeeded15 if(result?.errors?.length && !result?.data){16 // Total failure - mutation didn't execute17 throw new Error(result.errors.map((e)=> e.message).join("; "));18}1920 // Partial success - mutation executed but some fields had errors21 if(result?.errors?.length){22 console.warn("Account updated with warnings:", result.errors);23 // You can still use result.data24}2526 return result?.data?.uiapi?.AccountUpdate?.Record;27}
Error Handling in LWC and React
The approach to error handling in LWC and other frameworks such as React differs due to their distinct architectural models.
LWC: In addition to standard JavaScript error handling patterns, LWC provides an errorCallback(error, stack) lifecycle method in a parent component to catch errors from its children. When making @wire calls, errors are returned in an error object, which standardizes error handling in LWC.
React: Error handling typically relies on standard JavaScript mechanisms like promises for asynchronous Data SDK calls. You can also use error boundaries, which are special components (componentDidCatch, getDerivedStateFromError) that catch JavaScript errors in the component tree and display a fallback UI.
The error handling examples in the Multi-framework recipes repo demonstrate how to use error boundaries, handle GraphQL errors, and handle the loading, error, and empty states.