This guide explains how the Data Manager API handles and communicates errors.
Understanding the structure and meaning of API errors is crucial for building
robust applications that can gracefully handle issues, from invalid input to
temporary service unavailability.

The Data Manager API follows the standard Google API error model, which is based
on [gRPC Status codes](https://google.aip.dev/193). Each API response that results in an
error includes a `Status` object with:

- A numeric error code.
- An error message.
- Optional, additional error details.

## Canonical error codes

The Data Manager API uses a set of [canonical error codes](https://developers.google.com/data-manager/api/reference/rpc/google.rpc.md.txt#code) defined by
gRPC and HTTP. These codes provide a high-level indication of the error type.
You should always check this code first to understand the fundamental nature of
the problem.

For more details on these codes, see [API Design Guide - Error
codes](https://google.aip.dev/193).

## Fast-fail model

The Data Manager API uses a fast-fail model. If a request contains structural
errors or if any record fails validation for a required field, the entire
request fails, and the API does not process any of the data in that request.

> [!IMPORTANT]
> **Key Point:** The fast-fail model performs a limited set of validation checks to identify basic issues that would *entirely prevent* further processing of data. Validation failures for *optional fields* [don't cause the request to fail](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#ingestion-warnings). The Data Manager API also performs more complex validations asynchronously during processing, and reports any warnings or errors, along with the indexes of the affected records, in the [request status and diagnostics](https://developers.google.com/data-manager/api/devguides/diagnostics.md.txt).

### Comparison with the partial failure model

The fast-fail model differs from the partial failure model in some other Google
APIs, such as the
[Google Ads API](https://developers.google.com/google-ads/api/docs/best-practices/partial-failures.md.txt) and the
[Campaign Manager 360 API](https://developers.google.com/doubleclick-advertisers/guides/conversions_upload.md.txt#generate_an_insert_request).
In the partial failure model, a request
succeeds even if some records have errors, and the response contains error
details for the failed records.

Although partial failure can be convenient, it carries significant risks because
the partial failure model doesn't *proactively* alert you to errors---you
must explicitly check for errors in each response. This can mask important
issues because a request succeeds even if the API rejects many or even all of
the records in the request. If a significant portion of the records in a request
have errors but you don't inspect the response, you may be entirely unaware of
widespread issues with your data, and only discover those issues days or weeks
later when cumulative results don't align with your expectations.

The fast-fail model avoids these pitfalls by alerting you to issues with your
data or integration immediately so you can take appropriate action.

### Check for fast-fail errors with `validateOnly`

Most ingestion and removal requests support a `validateOnly` field. When you
set `validateOnly` to `true`, the Data Manager API runs the same basic validation
checks as it does for a regular request, but doesn't ingest or remove any data.

- If the request has errors, it fails with the same error response you would get from a regular request.
- If the request passes validation, it succeeds. The response includes any [`fieldWarnings`](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#ingestion-warnings) for optional fields, just like a regular request.

Use `validateOnly` to:

- Test a new or updated integration without affecting your live data.
- Confirm that a fix resolves an error before you resend the request.

> [!NOTE]
> **Note:** A successful validation-only request confirms that the request passes initial validation checks. It doesn't tell you if there will be errors or warnings during asynchronous processing. To identify asynchronous processing errors and warnings, send a live request and then [check request status and diagnostics](https://developers.google.com/data-manager/api/devguides/diagnostics.md.txt).

## Handle errors

Follow these steps when a request fails:

1. Check the [error code](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#canonical-error-codes) to find the type of error.

   - If you use gRPC, the error code is in the `code` field of the [`Status`](https://developers.google.com/data-manager/api/reference/rpc/google.rpc.md.txt#status). If you [use a client library](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#access-error-details), it may throw a specific type of exception that corresponds to the error code. For example, the client library for Java throws a `com.google.api.gax.rpc.InvalidArgumentException` if the error code is `INVALID_ARGUMENT`.
   - If you use REST, the error code is in the error response at `error.status`, and the corresponding HTTP status is at `error.code`.

   > [!IMPORTANT]
   > **Important:** The Data Manager API uses a [fast-fail model](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#fast-fail). If a request fails, none of the data is processed. Fix the error and resend the **entire** request.

2. Check for the [standard detail payload](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#standard-detail-payloads) for the
   error code. The standard detail payloads are a [set of messages for errors
   from Google APIs](https://google.aip.dev/193#statusdetails). They give you error details in a
   structured and consistent way. Each error from the Data Manager API may have
   multiple standard detail payload messages. The Data Manager API client libraries
   [have helper methods](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#access-error-details) to get the standard detail
   payloads from an error.

   No matter the error code, we recommend you check for and log the
   [`ErrorInfo`](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#error-info), [`RequestInfo`](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#request-id), [`Help`](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#help),
   and [`LocalizedMessage`](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#help) payloads.
   - `ErrorInfo` has information that might not be in other payloads.
   - `RequestInfo` has the request ID, which is helpful if you need to [contact support](https://developers.google.com/data-manager/api/support/contact).
   - `Help` and `LocalizedMessage` contain links and other details to help you address the error.

   In addition, the `BadRequest` payload is useful for `INVALID_ARGUMENT` errors
   since it provides information about which fields caused the error.

## Ingestion warnings

The Data Manager API accepts as much of an ingestion request as possible.
If you include data that is not required, validation failures for those fields
won't cause the request to fail. For example, if a cart item is missing a
merchant product ID, the API processes the rest of the request and returns a
warning.

A successful ingestion response (HTTP status code `200`) includes these warnings
in a `fieldWarnings` list. Each entry is a [`FieldWarning`](https://developers.google.com/data-manager/api/reference/rpc/google.ads.datamanager.v1.md.txt#fieldwarning) object with the
following fields:

`field`

:   The location of the field in the request, in snake case path syntax.

    If a path points to an item in a list (a `repeated` field), its index is
    shown in square brackets (`[...]`) after the list's name.

    For example, `events.events[0].cart_data.items[0].merchant_product_id` identifies a
    warning related to the first item in the cart data of the first event in the request.

`description`

:   An explanation of why the provided value caused a warning.

`reason`

:   The [`WarningReason`](https://developers.google.com/data-manager/api/reference/rpc/google.ads.datamanager.v1.md.txt#warningreason) enum value that identifies the type of warning.

> [!IMPORTANT]
> **Important:** Ingestion warnings won't cause the request to fail. You must check the response for `fieldWarnings` to understand if all of the data you sent was accepted.

### Example with `FieldWarning`

Here is a sample response for a successful ingestion request that contains a
warning because a merchant product ID was missing for one of the cart items.

```json
{
  "requestId": "126365e1-16d0-4c81-9de9-f362711e250a",
  "fieldWarnings": [
    {
      "field": "events.events[0].cart_data.items[0].merchant_product_id",
      "description": "The merchant product ID is missing in the cart item.",
      "reason": "WARNING_REASON_CART_DATA_ITEM_MERCHANT_PRODUCT_ID_MISSING"
    }
  ]
}
```

The `fieldWarnings` list covers issues found during initial validation. The
Data Manager API also reports warnings found during processing in
[diagnostics](https://developers.google.com/data-manager/api/devguides/diagnostics.md.txt), along with the
[indexes of the affected records](https://developers.google.com/data-manager/api/devguides/diagnostics.md.txt#record_indices).

## Standard detail payloads

The most common standard detail payloads for the Data Manager API are:

- [`BadRequest`](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#bad-request)
- [`RequestInfo`](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#request-id)
- [`ErrorInfo`](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#error-info)
- [`Help` and `LocalizedMessage`](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#help)

### `BadRequest`

Check for the [`BadRequest`](https://developers.google.com/data-manager/api/reference/rpc/google.rpc.md.txt#badrequest) payload when a request fails with
`INVALID_ARGUMENT` (HTTP status code `400`).

A `BadRequest` message shows that the request had fields with bad values, or was
missing a value for a required field. Check the `field_violations` list in the
`BadRequest` to find which fields have errors. Each `field_violations` entry
has information to help you fix the error:

`field`

:   The location of the field in the request, in snake case path syntax.

    If a path points to an item in a list (a `repeated` field), its index is
    shown in square brackets (`[...]`) after the list's name.

    For example, `destinations[0].operating_account.account_id` is the
    `account_id` in the `operating_account` of the first item in the
    `destinations` list.

`description`

:   An explanation of why the value caused an error.

`reason`

:   The [`ErrorReason`](https://developers.google.com/data-manager/api/reference/rpc/google.ads.datamanager.v1.md.txt#errorreason) enum, such as `INVALID_HEX_ENCODING` or
    `INVALID_CURRENCY_CODE`.

#### Examples of `BadRequest`

Here's a sample response for an `INVALID_ARGUMENT` error with a `BadRequest`
message. The `field_violations` show the error is an `accountId` that is not a
number. The `field` value `destinations[0].login_account.account_id` shows the
`accountId` with a field violation is in the `login_account` of the first item
in the `destinations` list.

```json
{
  "error": {
    "code": 400,
    "message": "There was a problem with the request.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "INVALID_ARGUMENT",
        "domain": "datamanager.googleapis.com",
        "metadata": {
          "requestId": "t-a8896317-069f-4198-afed-182a3872a660"
        }
      },
      {
        "@type": "type.googleapis.com/google.rpc.RequestInfo",
        "requestId": "t-a8896317-069f-4198-afed-182a3872a660"
      },
      {
        "@type": "type.googleapis.com/google.rpc.BadRequest",
        "fieldViolations": [
          {
            "field": "destinations[0].login_account.account_id",
            "description": "String is not a valid number.",
            "reason": "INVALID_NUMBER_FORMAT"
          }
        ]
      }
    ]
  }
}
```

Here's another sample response from an `INVALID_ARGUMENT` error with a
`BadRequest` message. In this case, the `field_violations` list shows two
errors:

1. The first `event` has a value that's not hex-encoded on the event's second
   [user identifier](https://developers.google.com/data-manager/api/reference/rpc/google.ads.datamanager.v1.md.txt#useridentifier).

2. The second `event` has a value that's not hex-encoded on the event's third
   [user identifier](https://developers.google.com/data-manager/api/reference/rpc/google.ads.datamanager.v1.md.txt#useridentifier).

```json
{
  "error": {
    "code": 400,
    "message": "There was a problem with the request.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "INVALID_ARGUMENT",
        "domain": "datamanager.googleapis.com",
        "metadata": {
          "requestId": "t-6bc8fb83-d648-4942-9c49-2604276638d8"
        }
      },
      {
        "@type": "type.googleapis.com/google.rpc.RequestInfo",
        "requestId": "t-6bc8fb83-d648-4942-9c49-2604276638d8"
      },
      {
        "@type": "type.googleapis.com/google.rpc.BadRequest",
        "fieldViolations": [
          {
            "field": "events.events[0].user_data.user_identifiers[1]",
            "description": "The HEX encoded value is malformed.",
            "reason": "INVALID_HEX_ENCODING"
          },
          {
            "field": "events.events[1].user_data.user_identifiers[2]",
            "description": "The HEX encoded value is malformed.",
            "reason": "INVALID_HEX_ENCODING"
          }
        ]
      }
    ]
  }
}
```

### `RequestInfo`

Check for the [`RequestInfo`](https://developers.google.com/data-manager/api/reference/rpc/google.rpc.md.txt#requestinfo) payload whenever a request fails. A `RequestInfo`
contains the `request_id` that uniquely identifies your API request.

```json
{
  "@type": "type.googleapis.com/google.rpc.RequestInfo",
  "requestId": "t-4490c640-dc5d-4c28-91c1-04a1cae0f49f"
}
```

When logging errors or [contacting support](https://developers.google.com/data-manager/api/support/contact), make sure to include
the request ID to help with diagnosing issues.

### `ErrorInfo`

Check for the [`ErrorInfo`](https://developers.google.com/data-manager/api/reference/rpc/google.rpc.md.txt#errorinfo) message to retrieve additional information that
may not be captured in the other standard detail payloads. The `ErrorInfo`
payload contains a `metadata` map with information about the error.

For example, here's the `ErrorInfo` for a `PERMISSION_DENIED` failure caused by
using credentials for a Google Cloud project where the
Data Manager API isn't enabled. The `ErrorInfo` provides additional information
about the error, such as:

- The project associated with the request, under `metadata.consumer`.
- The name of the service, under `metadata.serviceTitle`.
- The URL where the service can be enabled, under `metadata.activationUrl`.

```json
{
  "error": {
    "code": 403,
    "message": "Data Manager API has not been used in project PROJECT_NUMBER before or it is disabled. Enable it by visiting https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
    "status": "PERMISSION_DENIED",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "SERVICE_DISABLED",
        "domain": "googleapis.com",
        "metadata": {
          "consumer": "projects/PROJECT_NUMBER",
          "service": "datamanager.googleapis.com",
          "containerInfo": "PROJECT_NUMBER",
          "serviceTitle": "Data Manager API",
          "activationUrl": "https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER"
        }
      },
      ...
    ]
  }
}
```

#### Quota and rate limit errors

When a request exceeds a project limit, the API returns a `RESOURCE_EXHAUSTED`
error (HTTP status code `429`). The `ErrorInfo` payload provides details about
which limit was exceeded in its `metadata` map:

`consumer`
:   The Google Cloud project associated with the request,
    formatted as
    `projects/PROJECT_NUMBER`.

`quota_limit`
:   The name of the quota limit that was exceeded, such as
    `IngestionMutateRequestsPerMinutePerProject` or
    `IngestionMutateRequestsPerDayPerProject`. You can use this value to determine
    whether the application exceeded a per-minute limit or a daily limit. For the
    full list of limit names, see [Project limits](https://developers.google.com/data-manager/api/devguides/limits.md.txt#project_limits).

`quota_location`
:   The location where the quota is enforced. For the Data Manager API, this is
    always `global`.

`quota_metric`
:   The metric associated with the limit, such as
    `datamanager.googleapis.com/ingestion_mutate_requests`.

`service`
:   The name of the service, `datamanager.googleapis.com`.

Here's an example of a `RESOURCE_EXHAUSTED` error response when a request
exceeds the per-minute limit for ingestion mutate requests:

```json
{
  "error": {
    "code": 429,
    "message": "Quota exceeded for quota metric 'Ingestion mutate requests' and limit 'Ingestion mutate requests per minute' of service 'datamanager.googleapis.com' for consumer 'project_number:PROJECT_NUMBER'.",
    "status": "RESOURCE_EXHAUSTED",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "RATE_LIMIT_EXCEEDED",
        "domain": "googleapis.com",
        "metadata": {
          "consumer": "projects/PROJECT_NUMBER",
          "quota_limit": "IngestionMutateRequestsPerMinutePerProject",
          "quota_location": "global",
          "quota_metric": "datamanager.googleapis.com/ingestion_mutate_requests",
          "service": "datamanager.googleapis.com"
        }
      }
    ]
  }
}
```

### `Help` and `LocalizedMessage`

Check for the [`Help`](https://developers.google.com/data-manager/api/reference/rpc/google.rpc.md.txt#help) and [`LocalizedMessage`](https://developers.google.com/data-manager/api/reference/rpc/google.rpc.md.txt#localizedmessage) payloads to get links to
documentation and localized error messages that help you understand and fix the
error.

For example, here's the `Help` and `LocalizedMessage` for a `PERMISSION_DENIED`
failure caused by using credentials for a Google Cloud project
where the Data Manager API isn't enabled. The `Help` payload shows the URL where the
service can be enabled, and the `LocalizedMessage` has a description of the
error.

```json
{
  "error": {
    "code": 403,
    "message": "Data Manager API has not been used in project PROJECT_NUMBER before or it is disabled. Enable it by visiting https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
    "status": "PERMISSION_DENIED",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.LocalizedMessage",
        "locale": "en-US",
        "message": "Data Manager API has not been used in project PROJECT_NUMBER before or it is disabled. Enable it by visiting https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry."
      },
      {
        "@type": "type.googleapis.com/google.rpc.Help",
        "links": [
          {
            "description": "Google API Console API activation",
            "url": "https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER"
          }
        ]
      },
      ...
    ]
  }
}
```

## Access error details

If you are using one of the [client libraries](https://developers.google.com/data-manager/api/devguides/quickstart/set-up-access.md.txt), use the
helper methods to get the standard detail payloads.

### .NET

```c#
try {
    // Send API request
}
catch (Grpc.Core.RpcException rpcException)
{
    Console.WriteLine($"Exception encountered: {rpcException.Message}");
    var statusDetails =
        Google.Api.Gax.Grpc.RpcExceptionExtensions.GetAllStatusDetails(
            rpcException
        );
    foreach (var detail in statusDetails)
    {
        if (detail is Google.Rpc.BadRequest)
        {
            Google.Rpc.BadRequest badRequest = (Google.Rpc.BadRequest)detail;
            foreach (
                BadRequest.Types.FieldViolation? fieldViolation in badRequest.FieldViolations
            )
            {
                // Access attributes such as fieldViolation!.Reason and fieldViolation!.Field
            }
        }
        else if (detail is Google.Rpc.RequestInfo)
        {
            Google.Rpc.RequestInfo requestInfo = (Google.Rpc.RequestInfo)detail;
            string requestId = requestInfo.RequestId;
            // Log the requestId...
        }
        else if (detail is Google.Rpc.ErrorInfo)
        {
            Google.Rpc.ErrorInfo errorInfo = (Google.Rpc.ErrorInfo)detail;
            // Log the errorInfo.Reason and errorInfo.Metadata...

            // If handling a rate limit error, check the exceeded quota limit:
            if (errorInfo.Reason == "RATE_LIMIT_EXCEEDED" &&
                errorInfo.Metadata.TryGetValue("quota_limit", out string quotaLimit))
            {
                // Inspect quotaLimit to determine whether it is a per-minute
                // or daily limit (for example,
                // IngestionMutateRequestsPerMinutePerProject).
            }

            // Log the details in the 'Metadata' map...
            foreach (
                KeyValuePair<String, String> metadataEntry in errorInfo.Metadata
            )
            {
                // Log the metadataEntry.Key and metadataEntry.Value...
            }
        }
        else
        {
            // ...
        }
    }
}
```

### Java

```java
try {
  // Send API request
} catch (com.google.api.gax.rpc.InvalidArgumentException invalidArgumentException) {
  // Gets the standard BadRequest payload from the exception.
  BadRequest badRequest = invalidArgumentException.getErrorDetails().getBadRequest();
  for (int i = 0; i < badRequest.getFieldViolationsCount(); i++) {
    FieldViolation fieldViolation = badRequest.getFieldViolations(i);
    // Access attributes such as fieldViolation.getField() and fieldViolation.getReason()
  }

  // Gets the standard RequestInfo payload from the exception.
  RequestInfo requestInfo = invalidArgumentException.getErrorDetails().getRequestInfo();
  if (requestInfo != null) {
    String requestId = requestInfo.getRequestId();
    // Log the requestId...
  }
} catch (com.google.api.gax.rpc.ApiException apiException) {
  // Fallback exception handler for other types of ApiException.

  // Gets the standard ErrorInfo payload from the exception.
  ErrorInfo errorInfo = apiException.getErrorDetails().getErrorInfo();
  // Log the 'reason' and 'domain'...

  // If handling a rate limit error, check the exceeded quota limit:
  if (errorInfo != null && "RATE_LIMIT_EXCEEDED".equals(errorInfo.getReason())) {
    String quotaLimit = errorInfo.getMetadataMap().get("quota_limit");
    // Inspect quotaLimit to determine whether it is a per-minute
    // or daily limit (for example,
    // IngestionMutateRequestsPerMinutePerProject).
  }

  // Log the details in the 'metadata' map...
  for (Entry<String, String> metadataEntry : errorInfo.getMetadataMap().entrySet()) {
    // Log the metadataEntry key and value...
  }

  // Gets the standard RequestInfo payload from the exception.
  RequestInfo requestInfo = apiException.getErrorDetails().getRequestInfo();
  if (requestInfo != null) {
    String requestId = requestInfo.getRequestId();
    // Log the requestId...
  }
  ...
}
```

## Handle header size errors for gRPC

When a large number of records in an ingestion or removal request fail
validation, the Data Manager API returns all errors in the response's
[`BadRequest`](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#bad-request) payload.

In gRPC, the service returns these error details in the HTTP/2 response headers
(metadata). By default, gRPC client libraries limit incoming headers to a
maximum size (typically 8--10 KB). If error details exceed this limit, the
client-side HTTP/2 transport fails before parsing the error response, throwing
an exception such as:

```text
Http2Exception: Header size exceeded max allowed size (10240)
```

Because the client can't process the response headers, it cancels the stream
(which appears as HTTP status `499 Client Closed Request` in server-side logs),
and your application can't access the [`BadRequest`](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#bad-request) error
details.

> [!NOTE]
> **Note:** This issue only affects gRPC requests. REST requests aren't affected because REST returns error details in the response body rather than headers.

To resolve this issue and inspect the full list of errors, configure your gRPC
client with an increased maximum incoming metadata size (such as 16 MB):

### .NET

```
// Creates a client for the ingestion service. Increases the maximum metadata size to
// 16MB to prevent RESOURCE_EXHAUSTED errors when responses contain large error
// details.
IngestionServiceClient ingestionServiceClient = new IngestionServiceClientBuilder
{
    GrpcChannelOptions = GrpcChannelOptions.Empty.WithCustomOption(
        "grpc.max_metadata_size",
        16 * 1024 * 1024
    ),
}.Build();
```

### Java

```java
// Creates a client for the ingestion service. Increases the maximum metadata size to 16MB to
// prevent RESOURCE_EXHAUSTED errors when responses contain large error details.
InstantiatingGrpcChannelProvider provider =
    IngestionServiceSettings.defaultGrpcTransportProviderBuilder()
        .setMaxInboundMetadataSize(16 * 1024 * 1024)
        .build();
IngestionServiceSettings settings =
    IngestionServiceSettings.newBuilder().setTransportChannelProvider(provider).build();
try (IngestionServiceClient ingestionServiceClient = IngestionServiceClient.create(settings)) {
  // ...
}
```

### Node

```javascript
// Creates a client for the ingestion service. Increases the maximum metadata
// size to 16MB to prevent RESOURCE_EXHAUSTED errors when responses contain
// large error details.
const client = new IngestionServiceClient({
  'grpc.max_metadata_size': 16 * 1024 * 1024,
});
```

### PHP

```php
// Creates a client for the ingestion service. Increases the maximum metadata size to 16MB to
// prevent RESOURCE_EXHAUSTED errors when responses contain large error details.
$client = new IngestionServiceClient([
    'transportConfig' => [
        'grpc' => [
            'stubOpts' => [
                'grpc.max_metadata_size' => 16 * 1024 * 1024,
            ],
        ],
    ],
]);
```

### Python

```python
# Creates a client for the ingestion service. Increases the maximum
# metadata size to 16MB to prevent RESOURCE_EXHAUSTED errors when responses
# contain large error details.
channel = datamanager_v1.services.ingestion_service.transports.IngestionServiceGrpcTransport.create_channel(
    options=[("grpc.max_metadata_size", 16 * 1024 * 1024)]
)
transport = datamanager_v1.services.ingestion_service.transports.IngestionServiceGrpcTransport(
    channel=channel
)
client: datamanager_v1.IngestionServiceClient = (
    datamanager_v1.IngestionServiceClient(transport=transport)
)
```

## Best practices for error handling

To build resilient applications, implement the following best practices.

Validate before you send
:   When you build or change an integration, send requests with
    [`validateOnly`](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#validate-only) set to `true` to catch fast-fail errors
    before you ingest data.

Inspect error details
:   Always look for one of the [standard detail
    payloads](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors#standard-detail-payloads) such as `BadRequest`. Each standard
    detail payload contains information to help you understand the cause of the
    error.

Check diagnostics after ingestion
:   A successful ingestion request doesn't guarantee that every record was
    processed. [Retrieve diagnostics](https://developers.google.com/data-manager/api/devguides/diagnostics.md.txt) for each
    successful request, and use the
    [record indexes](https://developers.google.com/data-manager/api/devguides/diagnostics.md.txt#record_indices) in each warning
    and error to find and fix the affected records.

Differentiate client from server errors

:   Determine if the error is caused by an issue with your implementation (the
    client) or an issue with the API (the server).

    - Client errors: Codes like `INVALID_ARGUMENT`, `NOT_FOUND`, `PERMISSION_DENIED`, `FAILED_PRECONDITION`, `UNAUTHENTICATED`. These require changes to the request or your application's state/credentials. Don't retry the request without addressing the issue.
    - Server errors: Codes like `UNAVAILABLE`, `INTERNAL`, `DEADLINE_EXCEEDED`, `UNKNOWN`. These suggest a temporary issue with the API service.

Implement a retry strategy

:   Determine if the error can be retried, and use a retry strategy.

    - Retry *only* for transient server errors (such as `UNAVAILABLE`,
      `DEADLINE_EXCEEDED`, `INTERNAL`, `UNKNOWN`, and `ABORTED`) and
      per-minute rate limits (`RESOURCE_EXHAUSTED` with
      `RATE_LIMIT_EXCEEDED`).

    - For rate limits, inspect the [`quota_limit` in
      `ErrorInfo`](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors#quota-errors):

      - If the limit is **per-minute** (such as
        `IngestionMutateRequestsPerMinutePerProject`), pause requests and
        retry using exponential backoff with jitter.

      - If the limit is **daily** (such as
        `IngestionMutateRequestsPerDayPerProject`), don't retry
        immediately. Pause processing until the daily quota resets at
        midnight Pacific Time.

    - Use an [exponential backoff algorithm](https://en.wikipedia.org/wiki/Exponential_backoff) to wait for increasing periods
      between retries. This helps avoid overwhelming an already stressed
      service. For example, wait 1s, then 2s, then 4s, continuing up to a
      maximum number of retries or total wait time.

    - Add a small random amount of "jitter" to the backoff delays to prevent
      the "thundering herd" problem where many clients retry simultaneously.

Log thoroughly

:   Log the full error response, including all standard detail payloads,
    especially the [request ID](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#request-id). This information is essential for
    debugging and reporting issues to Google support if needed.

Provide user feedback

:   Based on the codes and messages in the
    [standard detail payloads](https://developers.google.com/data-manager/api/devguides/concepts/understand-errors.md.txt#standard-detail-payloads), provide clear and
    helpful feedback to your application's users. For example, instead of just "An
    error occurred," you can say "Transaction ID was missing" or "The account ID
    of the destination was not found."

By following these guidelines, you can effectively diagnose and handle errors
returned by the Data Manager API, leading to more stable and user-friendly
applications.