{"record":{"id":"5c9278ac49a9e744","repo":"apache/beam","slug":"more-than-d-attempts-to-call-appendrows-failed-last","errorCode":null,"errorMessage":"More than %d attempts to call AppendRows failed. Last encountered error: %s. Please check if the destination table exists and if the service account has the bigquery.tables.updateData permission.","messagePattern":"More than (.+?) attempts to call AppendRows failed\\. Last encountered error: (.+?)\\. Please check if the destination table exists and if the service account has the bigquery\\.tables\\.updateData permission\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/StorageApiWriteUnshardedRecords.java","lineNumber":804,"sourceCode":"                invalidateAppendClient(true);\n                allowedRetry = 5;\n              } else {\n                allowedRetry = 35;\n              }\n\n              // Maximum number of times we retry before we fail the work item.\n              if (failedContext.failureCount > allowedRetry) {\n                String errorMessage =\n                    String.format(\n                        \"More than %d attempts to call AppendRows failed. Last encountered error: %s\",\n                        allowedRetry, error != null ? error.toString() : \"unknown\");\n                if (statusCode == Status.Code.PERMISSION_DENIED\n                    || statusCode == Status.Code.NOT_FOUND) {\n                  errorMessage +=\n                      \". Please check if the destination table exists and if the service account has the \"\n                          + \"bigquery.tables.updateData permission.\";\n                }\n                throw new RuntimeException(errorMessage, error);\n              }\n\n              // The following errors are known to be persistent, so always fail the work item in\n              // this case.\n              if (statusCode.equals(Status.Code.OUT_OF_RANGE)\n                  || statusCode.equals(Status.Code.ALREADY_EXISTS)) {\n                throw new RuntimeException(\n                    \"Append to stream \"\n                        + this.streamName\n                        + \" failed with invalid \"\n                        + \"offset of \"\n                        + failedContext.offset);\n              }\n\n              // Schema mismatched exceptions can happen if the table was recently updated. Since\n              // vortex caches schemas\n              // we might see the new schema before vortex does. In this case, we simply need to\n              // retry.","sourceCodeStart":786,"sourceCodeEnd":822,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/StorageApiWriteUnshardedRecords.java#L786-L822","documentation":"RuntimeException thrown by StorageApiWriteUnshardedRecords.flush after all configured attempts to call the Storage Write API AppendRows RPC failed. The message includes the last gRPC error; for PERMISSION_DENIED or NOT_FOUND statuses an extra hint about the table's existence and bigquery.tables.updateData permission is appended.","triggerScenarios":"AppendRows fails on every retry attempt for a batch of rows — e.g. persistent PERMISSION_DENIED or NOT_FOUND statuses, gRPC unavailability, or schema mismatch — until the attempt counter exceeds the retry limit.","commonSituations":"Service account lacking bigquery.tables.updateData on the destination table; table/stream deleted while the pipeline runs; BigQuery Storage Write API outage or quota exhaustion; write stream closed/finalized unexpectedly.","solutions":["Read the wrapped cause and error message for the final gRPC status code","Grant the pipeline service account the bigquery.tables.updateData permission on the destination table","Confirm the destination table exists and wasn't deleted or renamed during the run","Check BigQuery quotas and Storage Write API health; retry the pipeline after transient outages","If schema changed on the table, update the writer schema or enable schema auto-update"],"exampleFix":"// before: service account has read-only access\n// after: grant write permission\nbq update --source project:dataset dataset  # then in IAM:\n# roles/bigquery.dataEditor on the dataset for the pipeline SA","handlingStrategy":"retry","validationCode":"com.google.cloud.bigquery.Table t = bigquery.getTable(tableId);\nif (t == null) throw new IllegalStateException(\"Destination table missing\");\n// check SA permission\nPolicy p = bigquery.getIamPolicy(tableId.getTableId().getDataset());\n// ensure the pipeline SA has roles/bigquery.dataEditor","typeGuard":null,"tryCatchPattern":"try {\n  pipeline.run().waitUntilFinish();\n} catch (RuntimeException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"attempts to call AppendRows failed\")) {\n    log.error(\"AppendRows exhausted retries; cause:\", e.getCause());\n    // fix permission/table issue indicated by final status, then resubmit\n  } else { throw e; }\n}","preventionTips":["Grant the pipeline SA bigquery.tables.updateData (e.g. roles/bigquery.dataEditor) on the dataset","Monitor table deletions during the run; avoid dropping tables under active pipelines","Watch BigQuery quota usage for the Storage Write API","Tune retry/backoff parameters for known transient outages"],"tags":["bigquery","storage-write","grpc","permission-denied","retry-exhausted","gcp"],"backgroundTag":"api-request-failed","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}