apache/beam · error · IOException

invalid table ID %s. Table IDs must be alphanumeric (plus un

Error message

invalid table ID %s. Table IDs must be alphanumeric (plus underscores) and must be at most 1024 characters long. Also, table decorators cannot be used.

What it means

validateWholeTableReference throws IOException when the table id does not match [-\w]{1,1024}, i.e. it contains characters other than letters, digits, underscores and hyphens, exceeds 1024 characters, or uses table decorators like 'table$20240101'. FakeDatasetService enforces BigQuery's table-id rules for mutation operations.

Source

Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/testing/FakeDatasetService.java:370

          tables.get(tableRef.getProjectId(), tableRef.getDatasetId());
      if (dataset == null) {
        throwNotFound(
            "Tried to get a dataset %s:%s, but no such table was set",
            tableRef.getProjectId(), tableRef.getDatasetId());
      }
      dataset.remove(tableRef.getTableId());
    }
  }

  /**
   * Validates a table reference for whole-table operations, such as create/delete/patch. Such
   * operations do not support partition decorators.
   */
  private static void validateWholeTableReference(TableReference tableReference)
      throws IOException {
    final Pattern tableRegexp = Pattern.compile("[-\\w]{1,1024}");
    if (!tableRegexp.matcher(tableReference.getTableId()).matches()) {
      throw new IOException(
          String.format(
              "invalid table ID %s. Table IDs must be alphanumeric "
                  + "(plus underscores) and must be at most 1024 characters long. Also, table"
                  + " decorators cannot be used.",
              tableReference.getTableId()));
    }
  }

  @Override
  public void createTable(Table table) throws IOException {
    TableReference tableReference = table.getTableReference();
    validateWholeTableReference(tableReference);
    synchronized (FakeDatasetService.class) {
      Map<String, TableContainer> dataset =
          tables.get(tableReference.getProjectId(), tableReference.getDatasetId());
      if (dataset == null) {
        throwNotFound(
            "Tried to get a dataset %s:%s, but no such table was set",

View on GitHub (pinned to 12126d8942)

Solutions

  1. Pass only the bare table id to setTableId (no project/dataset prefix, no decorators)
  2. Strip decorators/partition suffixes before validating (split on '$', '@', '/')
  3. Check length <= 1024 and characters [-\w] in your own code before invoking the fake

Example fix

// before
ref.setTableId("mytable$20240101");
service.createTable(ref, schema); // IOException
// after
ref.setTableId("mytable");
service.createTable(ref, schema);
Defensive patterns

Strategy: validation

Validate before calling

private static final Pattern TABLE_ID = Pattern.compile("[-\\w]{1,1024}");
if (!TABLE_ID.matcher(tableRef.getTableId()).matches()) {
  throw new IllegalArgumentException("invalid tableId: " + tableRef.getTableId());
}

Type guard

boolean isValidTableId(String id) {
  return id != null && id.matches("[-\\w]{1,1024}");
}

Try / catch

try {
  service.createTable(ref, schema);
} catch (IOException e) {
  if (e.getMessage() != null && e.getMessage().startsWith("invalid table ID")) {
    throw new IllegalArgumentException(e.getMessage(), e);
  }
  throw e;
}

Prevention

When it happens

Trigger: deleteTable, createTable, updateTableSchema, setPrimaryKey, or patchTableDescription called with a table id containing dots, slashes, spaces, decorator suffixes ('table@time'), or longer than 1024 chars.

Common situations: Passing 'project:dataset.table' as the table id instead of just the table id; appending partition/decorator syntax; interpolating an empty or URL-encoded id from config.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/0cbf8c624ee5e5bd. Report an issue: GitHub.