Error Handling in Data SDK

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  };
8
9if (result?.errors?.length) {
10  console.warn("GraphQL partial errors:", result.errors);
11}
12
13// result.data may contain partial data even when errors are present
14const 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 returned
2if (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.

Wrap transport errors and check the response.

1const dataSdk = await createDataSDK();
2let response: Response;
3try {
4	response = await dataSdk.fetch?("/services/apexrest/data-access/account-summary", {
5		method: "GET",
6		headers: { Accept: "application/json" },
7	});
8} catch (error) {
9	throw new Error(`Network/transport failure: ${(error as Error).message}`);
10}
11if (!response.ok) {
12	throw new Error(`Request failed: ${response.status} ${response.statusText}`);
13}
14const dataSdk = await response.json();

HTTP Errors 

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});
7
8if (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();
3
4  const result = await dataSdk.graphql?.mutate<UpdateAccountMutation>({
5    mutation: UPDATE_ACCOUNT,
6    variables: {
7      input: {
8        Id: id,
9        Account: updates,
10      },
11    },
12  });
13
14  // Check if the mutation succeeded
15  if (result?.errors?.length && !result?.data) {
16    // Total failure - mutation didn't execute
17    throw new Error(result.errors.map((e) => e.message).join("; "));
18  }
19
20  // Partial success - mutation executed but some fields had errors
21  if (result?.errors?.length) {
22    console.warn("Account updated with warnings:", result.errors);
23    // You can still use result.data
24  }
25
26  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.

Tip

See Also