{"record":{"id":"f0e0a18684a083a0","repo":"apache/iceberg","slug":"etag-of-legacy-table-s-is-empty-manually-update","errorCode":null,"errorMessage":"Etag of legacy table %s is empty, manually update the table via the BigQuery API or recreate and retry","messagePattern":"Etag of legacy table (.+?) is empty, manually update the table via the BigQuery API or recreate and retry","errorType":"validation","errorClass":"ValidationException","httpStatus":null,"severity":"error","filePath":"bigquery/src/main/java/org/apache/iceberg/gcp/bigquery/BigQueryTableOperations.java","lineNumber":165,"sourceCode":"  }\n\n  private void addConnectionIfProvided(Table tableBuilder, Map<String, String> metadataProperties) {\n    if (metadataProperties.containsKey(TABLE_PROPERTIES_BQ_CONNECTION)) {\n      tableBuilder\n          .getExternalCatalogTableOptions()\n          .setConnectionId(metadataProperties.get(TABLE_PROPERTIES_BQ_CONNECTION));\n    }\n  }\n\n  /** Update table properties with concurrent update detection using etag. */\n  private void updateTable(String newMetadataLocation, TableMetadata metadata) {\n    Preconditions.checkState(\n        metastoreTable != null,\n        \"Table %s must be loaded during refresh before commit\",\n        tableName());\n\n    if (metastoreTable.getEtag().isEmpty()) {\n      throw new ValidationException(\n          \"Etag of legacy table %s is empty, manually update the table via the BigQuery API or\"\n              + \" recreate and retry\",\n          tableName());\n    }\n    ExternalCatalogTableOptions options = metastoreTable.getExternalCatalogTableOptions();\n    addConnectionIfProvided(metastoreTable, metadata.properties());\n\n    options.setParameters(buildTableParameters(newMetadataLocation, metadata));\n    client.update(tableReference, metastoreTable);\n    this.metastoreTable = null;\n  }\n\n  // To make the table queryable from Hive, the user would likely be setting the HIVE_ENGINE_ENABLED\n  // parameter.\n  //\n  // TODO: We need to make a decision on how to make the table queryable from Hive.\n  // (could be a server side change or a client side change - that's TBD).\n  private Table makeNewTable(TableMetadata metadata, String metadataFileLocation) {","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/bigquery/src/main/java/org/apache/iceberg/gcp/bigquery/BigQueryTableOperations.java#L147-L183","documentation":"updateTable requires a non-empty etag on the loaded legacy BigQuery Metastore table to perform an etag-based conditional update. If the etag is empty the library cannot do a safe compare-and-swap, so it throws ValidationException telling you to update the table manually via the BigQuery API or recreate it.","triggerScenarios":"doCommit -> updateTable on a metastore table whose ExternalCatalogTableOptions / etag field came back empty from the BigQuery Metastore API — typically a table created outside Iceberg's normal flow or mutated by another tool.","commonSituations":"A legacy table migrated/created without etag populated; the table was modified by non-Iceberg tooling that cleared it; the metastore returned a degraded/incomplete table resource.","solutions":["Update the table manually through the BigQuery API to fix its state, as the message suggests, then retry the commit.","Drop and recreate the Iceberg table in the BigQuery Metastore so a fresh etag is populated.","Refresh the table (doRefresh) before committing so the latest etag is loaded.","Check whether another tool is stripping the etag and stop that mutation path."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before committing, ensure a refreshed table with a usable etag:\nTableOperations ops = ((HasTableOperations) table).operations();\nops.refresh();\nBigQueryMetastoreTable metastoreTable = /* loaded via ops.refresh() */;\nif (metastoreTable == null || metastoreTable.getEtag() == null || metastoreTable.getEtag().isEmpty()) {\n  throw new IllegalStateException(\"Table has no etag; fix via BigQuery API or recreate before committing\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  table.refresh();\n} catch (ValidationException e) {\n  if (e.getMessage().contains(\"Etag\")) {\n    throw new IllegalStateException(\"Recreate or manually fix the BigQuery Metastore table: \" + e.getMessage(), e);\n  }\n  throw e;\n}","preventionTips":["Always refresh() the table before commit so a fresh etag is loaded","Avoid mutating BigQuery Metastore table options with non-Iceberg tooling","Recreate tables that were migrated by hand instead of patching them","Alert on empty etag at registration time rather than at first commit"],"tags":["bigquery","etag","validation","concurrency"],"backgroundTag":"empty-required-field","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}