> ## Documentation Index
> Fetch the complete documentation index at: https://docs.startree.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Hive Metastore: Onboarding via API

> Onboard an Iceberg table or a native Hive Parquet table registered in Hive Metastore as a StarTree External Table. The full Hive Metastore reference: requirements, every option, limits and errors.

> [!WARNING]
>
> This feature requires StarTree Cloud 0.18.0 or later. Contact StarTree support to enable the Hive Metastore catalog for your environment.

The Hive Metastore (HMS) catalog lets an External Table read a table registered in HMS. StarTree connects straight to the HMS Thrift endpoint (`catalogType=hive`), with no REST proxy in between. It reads Iceberg tables through their snapshots, and native Hive Parquet tables through their registered partitions and files.

This page onboards a table with the StarTree controller REST APIs, and it's the full reference for Hive Metastore: requirements, every option, limits and errors. Prefer a single statement? [Onboarding via SQL DDL](https://docs.startree.ai/corecapabilities/external-table/hive/onboarding-sql-ddl.md) creates the same table with `CREATE TABLE ... WITH`; each option's SQL DDL name is in the [configuration reference](#configuration-reference) below.

## What changes

Before StarTree Cloud 0.18.0, catalog-backed External Tables needed an Iceberg REST catalog (REST, Unity, Glue, S3 Tables). Now you can point at HMS directly:

* Set `catalogType` to `hive` and give a `thrift://host:port` URI in `catalog.hive.metastoreUri`.
* Authenticate to HMS with Kerberos, a delegation token, TLS, or mutual TLS through the `catalog.hive.auth.metastore.*` keys. REST auth options are rejected.
* Read **native Hive Parquet tables**, which have no Iceberg metadata.
* The sync always runs on the controller. The periodic watcher (`enabled = true`) is not supported.

You don't say whether a table is Iceberg or native Parquet. StarTree reads it from HMS, and the format decides how the table syncs.

| | Iceberg in HMS | Native Hive Parquet |
| :- | :- | :- |
| How HMS identifies it | `table_type=ICEBERG` and a `metadata_location` | Parquet input format, not transactional |
| SQL DDL `type` | `iceberg` | `hive` |
| Sync model | Iceberg snapshots | Append-only file listing. Each scan re-lists the registered locations and picks up new files |
| Starting snapshot | Latest (default), earliest, or a snapshot ID | Latest only |
| Row deletes | v2 equality and position deletes, v3 deletion vectors. Needs `enableDeletionVectors = true` (see [Deletion Vectors](https://docs.startree.ai/corecapabilities/external-table/deletion-vectors.md)) | Not supported. Overwritten, deleted, or dropped files are not reconciled |
| [Segment groups](https://docs.startree.ai/corecapabilities/external-table/segment-groups.md) | On by default for new tables | Not supported |
| Partition pruning | Opt in with `enablePartitionPruning = true` (imports the Iceberg partition spec) | No partition spec is imported. Partition columns are ordinary filterable columns |
| Partition columns | From the Iceberg schema | HMS partition keys become columns, even when absent from the Parquet files |
| Schema source | Iceberg schema | HMS column declarations, checked against the first Parquet file |
| Schema evolution | Not automatic | Not automatic |
| Storage | S3 (`s3://`, `s3a://`) or Azure Blob/ADLS with a SAS token | S3 (`s3://`, `s3a://`) or Azure Blob/ADLS with a SAS token |
| Data needed at create time | Iceberg metadata | At least one Parquet file |

## Requirements

* StarTree Cloud 0.18.0 or later, with the Hive Metastore catalog enabled by StarTree support.
* An HMS binary Thrift endpoint, written as `thrift://host:port`. Use `thrift://` even when TLS is on. Separate several endpoints with commas. HTTP endpoints are not supported.
* Tested HMS releases: Hive 2.3.9, 3.1.3, 4.0.1, 4.1.0 and 4.2.1. Stock Hive 2.3.9 can't combine Kerberos and TLS on one listener; the other releases support it with `saslQop` set to `auth`.
* Network access from your StarTree environment to HMS. StarTree also needs read access to the source data.
* For S3: read access to the table's files (for Iceberg, also its metadata, manifests and delete files). For native tables, also `s3:ListBucket` on the registered locations. If you don't set keys, the default AWS credentials of your environment are used.
* For Azure: Azure source storage enabled by StarTree support, and a container SAS token with read and list permissions.
* For Kerberos: contact StarTree support to configure your realm and KDC (`krb5.conf`). There is no per-table `krb5.conf` option. StarTree also needs DNS and KDC access.
* Keytabs, ticket caches, truststores and keystores are local files referenced by path. Contact StarTree support to provision them for your environment.
* Passwords and tokens can be passed as `srn2:secret#...` secret references.

## How it works

Onboarding is four calls. Each one feeds the next:

```text theme={null}
1. Validate & browse   POST /connections/browse   →  connect to HMS; list databases and tables
2. Preview             POST /tables/preview       →  infer schema + enriched table config
3. Create schema       POST /schemas              →  register the schema
4. Create table        POST /tables               →  register the External Table
```

> [!NOTE]
>
> All paths are relative to your data-plane base URL. Set `export BASE_URL=https://dp.<data-plane-id>.cp.<region>.startree.cloud/api/pinot`, the StarTree Cloud data-plane proxy that fronts the controller.
>
> If your controller requires authentication, add an `Authorization` header to every request, for example `-H "Authorization: Bearer <token>"`. The examples below leave it out.

## Step 1: Validate and browse the connection

**`POST /connections/browse`**

Browsing is the connection test. A `200` with an `items` list, even an empty one, means StarTree reached HMS and authenticated.

* Set `path` to `""` to list the **databases**.
* Set `path` to a database name to list its **tables**.

Browse checks the metastore connection only. Storage access is checked at preview, once a table is picked, because one HMS can hold Iceberg and Parquet tables that read storage differently.

### Request

| Field | Type | Required | Description |
| - | - | - | - |
| `connection.type` | string | Yes | `CATALOG` |
| `connection.params` | object | Yes | `catalogType: "hive"`, `catalog.hive.metastoreUri`, and any `catalog.hive.auth.metastore.*` keys. See [Metastore options](#metastore-options) |
| `path` | string | No | `""` or omitted for the database list. A database name for its tables |

> [tab] No authentication
>
> ```bash theme={null}
> curl -X POST "$BASE_URL/connections/browse" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "connection": {
>       "type": "CATALOG",
>       "params": {
>         "catalogType": "hive",
>         "catalog.hive.metastoreUri": "thrift://hms.example.com:9083"
>       }
>     },
>     "path": ""
>   }'
> ```

> [tab] Kerberos + TLS
>
> ```bash theme={null}
> curl -X POST "$BASE_URL/connections/browse" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "connection": {
>       "type": "CATALOG",
>       "params": {
>         "catalogType": "hive",
>         "catalog.hive.metastoreUri": "thrift://hms.example.com:9083",
>         "catalog.hive.auth.metastore.authType": "kerberos",
>         "catalog.hive.auth.metastore.principal": "pinot@EXAMPLE.COM",
>         "catalog.hive.auth.metastore.serverPrincipal": "hive/_HOST@EXAMPLE.COM",
>         "catalog.hive.auth.metastore.keytab": "/etc/hms/pinot.keytab",
>         "catalog.hive.auth.metastore.saslQop": "auth",
>         "catalog.hive.auth.metastore.tlsEnabled": "true",
>         "catalog.hive.auth.metastore.truststorePath": "/etc/hms/truststore.p12",
>         "catalog.hive.auth.metastore.truststoreType": "PKCS12",
>         "catalog.hive.auth.metastore.truststorePassword": "srn2:secret#hms-tls"
>       }
>     },
>     "path": ""
>   }'
> ```

> [tab] Delegation token
>
> ```bash theme={null}
> curl -X POST "$BASE_URL/connections/browse" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "connection": {
>       "type": "CATALOG",
>       "params": {
>         "catalogType": "hive",
>         "catalog.hive.metastoreUri": "thrift://hms.example.com:9083",
>         "catalog.hive.auth.metastore.authType": "delegation-token",
>         "catalog.hive.auth.metastore.delegationToken": "srn2:secret#hms-token"
>       }
>     },
>     "path": ""
>   }'
> ```

Passwords and tokens accept `srn2:secret#<name>` references. The secret's data key must be the config key, for example `catalog.hive.auth.metastore.truststorePassword`.

### Response

```json theme={null}
{
  "items": [
    { "name": "analytics", "type": "NAMESPACE" },
    { "name": "default", "type": "NAMESPACE" }
  ]
}
```

With `"path": "analytics"`, the items are that database's tables, each with `"type": "TABLE"`.

The table list comes straight from HMS, so it includes tables StarTree can't onboard, such as ORC, text or transactional (ACID) tables. Those fail at preview with a message naming the reason.

***

## Step 2: Preview the schema

**`POST /tables/preview`**

Samples the table, infers a Pinot schema, and returns a complete table config. Send a minimal OFFLINE table config whose `ExternalTableSyncTask` block describes the source. The request and response fields are the same as for other catalogs; see [Unity Catalog: Preview the schema](https://docs.startree.ai/corecapabilities/external-table/unity/onboarding-api.md#step-2-preview-the-schema) for the full field list.

> [tab] Iceberg in HMS (S3)
>
> ```bash theme={null}
> curl -X POST "$BASE_URL/tables/preview" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "tableConfig": {
>       "tableName": "hms_events_OFFLINE",
>       "tableType": "OFFLINE",
>       "task": {
>         "taskTypeConfigsMap": {
>           "ExternalTableSyncTask": {
>             "catalogType": "hive",
>             "executor": "controller",
>             "schedule": "0 */5 * * * ?",
>             "catalog.hive.metastoreUri": "thrift://hms.example.com:9083",
>             "catalog.hive.table.namespace": "analytics",
>             "catalog.hive.table.tableName": "iceberg_events",
>             "catalog.hive.auth.storage.region": "us-west-2"
>           }
>         }
>       }
>     },
>     "config": { "inference": { "embeddedSchemaEnabled": true } }
>   }'
> ```

> [tab] Native Hive Parquet (S3)
>
> ```bash theme={null}
> curl -X POST "$BASE_URL/tables/preview" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "tableConfig": {
>       "tableName": "hive_events_OFFLINE",
>       "tableType": "OFFLINE",
>       "task": {
>         "taskTypeConfigsMap": {
>           "ExternalTableSyncTask": {
>             "catalogType": "hive",
>             "executor": "controller",
>             "schedule": "0 */5 * * * ?",
>             "catalog.hive.metastoreUri": "thrift://hms.example.com:9083",
>             "catalog.hive.table.namespace": "analytics",
>             "catalog.hive.table.tableName": "events",
>             "catalog.hive.auth.storage.region": "us-west-2"
>           }
>         }
>       }
>     },
>     "config": { "inference": { "embeddedSchemaEnabled": true } }
>   }'
> ```

> [tab] Azure storage
>
> Replace the `catalog.hive.auth.storage.*` keys with:
>
> ```json theme={null}
> "sourceStorageProvider": "azure",
> "externalTableAccessPolicy": "azure-sas",
> "catalog.hive.auth.storage.sasToken": "srn2:secret#events-sas",
> "catalog.hive.auth.storage.credentialPrefix": "wasbs://events@myaccount.blob.core.windows.net/analytics/events"
> ```
>
> Azure source storage is in beta. Contact StarTree support to enable it.

> [!NOTE]
>
> * **Namespace and table** come from the browse response: the `NAMESPACE` you picked and one of its `TABLE`s.
> * **Keep `executor: controller` and leave out `enabled`.** The Hive catalog doesn't support the periodic watcher (`enabled = true`); a config with it is rejected.
> * **`schedule`** sets how often the table re-syncs. Without it, nothing runs on its own. It takes a Quartz cron expression.
> * **Row deletes (Iceberg only):** add `"enableDeletionVectors": "true"` if the Iceberg table has deletes. Without it, deletes are not applied. See [Deletion Vectors](https://docs.startree.ai/corecapabilities/external-table/deletion-vectors.md).
> * **Partition pruning (Iceberg only):** add `"enablePartitionPruning": "true"` to import the partition spec. See [Partition pruning](https://docs.startree.ai/corecapabilities/external-table/partition-pruning.md).
> * **Native Hive tables** need at least one Parquet file. The schema comes from the HMS columns, and each HMS partition key becomes a column.

### Response

Use `schema` in step 3 and `tableConfigs.offline` in step 4. Secrets are masked as `*****`.

For an Iceberg table, the returned config has [segment groups](https://docs.startree.ai/corecapabilities/external-table/segment-groups.md) turned on. A native Hive table never gets them, because native tables don't support them.

***

## Step 3: Create the schema

**`POST /schemas`**

Send the `schema` object from the preview response, with `schemaName` set to your table name.

```bash theme={null}
curl -X POST "$BASE_URL/schemas" \
  -H "Content-Type: application/json" \
  -d @schema.json
```

***

## Step 4: Create the table

**`POST /tables`**

Send `tableConfigs.offline` from the preview response.

```bash theme={null}
curl -X POST "$BASE_URL/tables" \
  -H "Content-Type: application/json" \
  -d @tableConfig.json
```

Creating the table doesn't start a sync. The first one runs at the next `schedule` tick. To start it now:

```bash theme={null}
curl -X POST "$BASE_URL/tasks/schedule?taskType=ExternalTableSyncTask&tableName=hms_events_OFFLINE"
```

***

## Quickstart

The whole flow as one script, for an Iceberg table in an HMS without authentication. Fill in the variables at the top.

```bash theme={null}
#!/usr/bin/env bash
set -euo pipefail

BASE_URL="https://dp.<data-plane-id>.cp.<region>.startree.cloud/api/pinot"
AUTH="Authorization: Bearer <token>"
HMS_URI="thrift://hms.example.com:9083"
DATABASE="analytics"
TABLE="iceberg_events"
PINOT_TABLE="hms_events"
STORAGE_REGION="us-west-2"

# 1. Browse the database: confirms the connection and that the table is there.
curl -sf -X POST "$BASE_URL/connections/browse" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  --data-raw "{
    \"connection\": {
      \"type\": \"CATALOG\",
      \"params\": {
        \"catalogType\": \"hive\",
        \"catalog.hive.metastoreUri\": \"$HMS_URI\"
      }
    },
    \"path\": \"$DATABASE\"
  }" | jq .

# 2. Preview.
curl -sf -X POST "$BASE_URL/tables/preview" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  --data-raw "{
    \"tableConfig\": {
      \"tableName\": \"${PINOT_TABLE}_OFFLINE\",
      \"tableType\": \"OFFLINE\",
      \"task\": {
        \"taskTypeConfigsMap\": {
          \"ExternalTableSyncTask\": {
            \"catalogType\": \"hive\",
            \"executor\": \"controller\",
            \"schedule\": \"0 */5 * * * ?\",
            \"catalog.hive.metastoreUri\": \"$HMS_URI\",
            \"catalog.hive.table.namespace\": \"$DATABASE\",
            \"catalog.hive.table.tableName\": \"$TABLE\",
            \"catalog.hive.auth.storage.region\": \"$STORAGE_REGION\"
          }
        }
      }
    },
    \"config\": { \"inference\": { \"embeddedSchemaEnabled\": true } }
  }" > preview.json

# 3. Extract the schema and table config.
jq --arg t "$PINOT_TABLE" '.schema | .schemaName = $t' preview.json > schema.json
jq '.tableConfigs.offline' preview.json > tableConfig.json

# 4. Create the schema and the table.
curl -sf -X POST "$BASE_URL/schemas" -H "$AUTH" -H 'Content-Type: application/json' -d @schema.json
curl -sf -X POST "$BASE_URL/tables"  -H "$AUTH" -H 'Content-Type: application/json' -d @tableConfig.json

# 5. Start the first sync now instead of waiting for the schedule.
curl -sf -X POST "$BASE_URL/tasks/schedule?taskType=ExternalTableSyncTask&tableName=${PINOT_TABLE}_OFFLINE" -H "$AUTH"
```

## Configuration reference

These keys go in the `ExternalTableSyncTask` block of the table config (the browse request takes the `catalog.hive.*` ones too). The **SQL DDL option** column is the name to use in `CREATE TABLE ... WITH` on the [SQL DDL page](https://docs.startree.ai/corecapabilities/external-table/hive/onboarding-sql-ddl.md).

### Catalog, table and sync options

| Task config key | SQL DDL option | Default | Allowed values |
| :- | :- | :- | :- |
| (SQL DDL only) | `type` | required | `iceberg` or `hive`. Must match the table format in HMS. The API reads the format from HMS |
| `catalogType` | `catalog_type` | required | `hive`. In SQL DDL, `type = 'hive'` implies it |
| `catalog.hive.metastoreUri` | `catalog_uri` | required | `thrift://host:port[,thrift://host:port]` |
| `catalog.hive.warehouse` | `catalog_name` | none | Optional |
| `catalog.hive.table.namespace` | `schema_name` | required | HMS database |
| `catalog.hive.table.tableName` | `table_name` | required. SQL DDL defaults it to the Pinot table name | HMS table |
| `schedule` | `refresh_interval` | none (no schedule) | Quartz cron expression. SQL DDL also takes `Ns` / `Nm` (1–59), `Nh` (1–23), `Nd` (1–31) |
| `iceberg.snapshotProcessing.initialVersionSelector` | `snapshot_mode` | `LATEST` | `LATEST`, `EARLIEST`, `SNAPSHOT_ID` (SQL DDL: lowercase). Native Hive: latest only |
| `iceberg.snapshotProcessing.initialSnapshotId` | `snapshot_id` | none | A snapshot ID. Used with `SNAPSHOT_ID` |
| `enablePartitionPruning` | `enable_partition_pruning` | `false` | `true`, `false` |
| `catalog.hive.validateStorage` | `validate_storage` | storage is validated | `false` to skip the storage checks at create time |
| Table replication | `replication` | cluster default | Integer |
| (SQL DDL only) | `enable_schema_evolution`, `enable_metadata_caching` | n/a | Accepted with a warning. No effect |

### Metastore options

| Task config key | SQL DDL option | Default | Allowed values |
| :- | :- | :- | :- |
| `catalog.hive.auth.metastore.authType` | `metastore.auth_type` | `none` | `none`, `kerberos`, `delegation-token` |
| `catalog.hive.auth.metastore.principal` | `metastore.principal` | none | Kerberos client principal with realm, `user[/instance]@REALM`. Required for Kerberos |
| `catalog.hive.auth.metastore.serverPrincipal` | `metastore.server_principal` | none | `service/host@REALM`, for example `hive/_HOST@EXAMPLE.COM`. `_HOST` becomes each endpoint's hostname. Required for Kerberos |
| `catalog.hive.auth.metastore.keytab` | `metastore.keytab` | none | Local keytab path. Do not combine with `ticket_cache` |
| `catalog.hive.auth.metastore.ticketCache` | `metastore.ticket_cache` | JVM default cache, if neither is set | Local ticket-cache path, without the `FILE:` prefix |
| `catalog.hive.auth.metastore.delegationToken` | `metastore.delegation_token` | none | Hadoop token encoded with `Token.encodeToUrlString()`. Required for `delegation-token` |
| `catalog.hive.auth.metastore.saslQop` | `metastore.sasl_qop` | `auth` | `auth`, `auth-int`, `auth-conf`. Must be `auth` with TLS. Not allowed with `auth_type = 'none'` |
| `catalog.hive.auth.metastore.tlsEnabled` | `metastore.tls_enabled` | `false` | `true`, `false` |
| `catalog.hive.auth.metastore.truststorePath` | `metastore.truststore_path` | JVM default trust | Local file path |
| `catalog.hive.auth.metastore.truststorePassword` | `metastore.truststore_password` | empty | Needs `truststore_path` |
| `catalog.hive.auth.metastore.truststoreType` | `metastore.truststore_type` | `JKS` | For example `JKS`, `PKCS12`. Needs `truststore_path` |
| `catalog.hive.auth.metastore.keystorePath` | `metastore.keystore_path` | none | Client certificate store for mutual TLS |
| `catalog.hive.auth.metastore.keystorePassword` | `metastore.keystore_password` | empty | Needs `keystore_path` |
| `catalog.hive.auth.metastore.keystoreType` | `metastore.keystore_type` | `JKS` | Needs `keystore_path` |
| `catalog.hive.auth.metastore.keyPassword` | `metastore.key_password` | The keystore password | Needs `keystore_path` |
| `catalog.hive.auth.metastore.tlsProtocols` | `metastore.tls_protocols` | `TLSv1.2,TLSv1.3` | `TLSv1.2`, `TLSv1.3` |
| `catalog.hive.auth.metastore.protocol` | `metastore.protocol` | `binary` | `binary`, `compact`. Must match the server |
| `catalog.hive.auth.metastore.framedTransport` | `metastore.framed_transport` | `false` | `true`, `false`. Not allowed with Kerberos or tokens |

TLS options other than `tlsEnabled` are rejected unless `tlsEnabled` is `true`. Hostname and certificate checks are always on. Authentication and certificate failures reject the connection; there is no plaintext fallback.

### Storage options

> [!WARNING]
>
> **Azure source storage is in beta.** Contact StarTree support to enable it for your environment.

| Task config key | SQL DDL option | Default | Allowed values |
| :- | :- | :- | :- |
| `sourceStorageProvider` | `storage.provider` | S3 | `azure` for Azure. Leave out for S3 |
| `catalog.hive.auth.storage.region` | `storage.region` | none | AWS region. S3 only |
| `catalog.hive.auth.storage.accessKeyId` | `storage.access_key` | Default AWS credentials | S3 only |
| `catalog.hive.auth.storage.secretAccessKey` | `storage.secret_key` | Default AWS credentials | S3 only |
| `catalog.hive.auth.storage.sessionToken` | `storage.session_token` | none | Temporary AWS credentials. S3 only |
| `catalog.hive.auth.storage.endpoint` | `storage.endpoint` | AWS | S3-compatible endpoint. S3 only |
| `catalog.hive.auth.storage.sasToken` | `storage.sas_token` | none | Required for Azure. Use a secret reference |
| `catalog.hive.auth.storage.credentialPrefix` | `storage.credential_prefix` | none | Required for Azure. Container or directory URI that covers every source file |

For Azure, also set `externalTableAccessPolicy` to `azure-sas`. SQL DDL sets it for you.

Passwords, tokens and keys accept `srn2:secret#<name>` references. The secret's data key must be the task config key, for example `catalog.hive.auth.metastore.truststorePassword`.

Create a new table if the HMS table changes between native Parquet and Iceberg.

## Query

Queries are standard SQL. Nothing is HMS-specific.

```sql theme={null}
SELECT COUNT(*) FROM hms_events;
```

For a native Hive table, each HMS partition key is a column. Its value comes from HMS, even when the Parquet files don't contain that column. If a file does contain it, the HMS value wins.

```sql theme={null}
SELECT event_day, COUNT(*)
FROM hive_events
WHERE event_day >= '2026-10-01'
GROUP BY event_day
ORDER BY event_day;
```

The HMS default partition (`__HIVE_DEFAULT_PARTITION__`) reads as `NULL`. If your HMS uses a different marker, set `catalog.hive.defaultPartitionName` in the task config.

After you register a new partition in HMS, it shows up on a later sync. Write new data to new file paths. Pinot does not pick up changes to files it has already read.

For Iceberg tables, each sync loads new snapshots. Rows from a new snapshot show up after that sync finishes.

## Indexes

Index support is the same as for other External Tables (see [Indexes](https://docs.startree.ai/corecapabilities/external-table/indexes.md)). Nothing here is specific to HMS.

## Verify

* `GET $BASE_URL/tables/hms_events_OFFLINE/externalTable/status`. Check that `snapshotIngestion.lastRunStatus` is `COMPLETED` and `lastRunError` is empty. HTTP 200 alone doesn't mean the sync worked. See [Observability](https://docs.startree.ai/corecapabilities/external-table/observability.md).

* `GET $BASE_URL/tables/hms_events_OFFLINE/externalTable/checkpoint`. Wait for `"state": "DONE"`.

* Run `SELECT COUNT(*) FROM hms_events` and compare it with the source.

* If nothing ever syncs, start the task by hand (see [Step 4](#step-4-create-the-table)). If it still doesn't run, contact StarTree support.

## Limits

* Only the binary Thrift endpoint works. No HTTP, and no `thrift+ssl://` (use `metastore.tls_enabled`).
* The sync runs on the controller only. The periodic watcher (`enabled = true`) is not supported.
* Storage must be S3 (`s3://`, `s3a://`) or Azure Blob/ADLS with a SAS token. Locations with credentials or query strings in the URI are rejected.
* Native Hive: Parquet only. Transactional (ACID) tables are not supported.
* Native Hive: append-only. Overwrites, deletes, dropped partitions and changed partition values are not reconciled.
* Native Hive: no snapshots, row deletes, segment groups or `snapshot_mode` other than `latest`.
* Native Hive: the table needs at least one Parquet file at create time.
* Native Hive: `array`, `map` and `struct` columns are not supported. `char(n)` and `varchar(n)` work only for partition keys. Data columns must use `string`.
* Native Hive: Parquet column names must match HMS column names exactly, including case. `timestamp` data columns must use a local timestamp (`isAdjustedToUTC=false`) or INT96. `decimal(p,s)` must match the Parquet precision and scale.
* Schema changes in the source are not applied automatically.
* To switch a table between native Parquet and Iceberg, create a new Pinot table.
* StarTree does not renew delegation tokens.
* Stock Hive 2.3.9 can't use Kerberos and TLS together on one listener.

## Common errors

Errors raised while connecting to HMS can come wrapped as `Failed to resolve the external table '<db>.<table>' from the catalog: <message>`. Look for the message. Some messages name a SQL DDL option; those come from `POST /sql/ddl`.

| Error | Cause | Fix |
| :- | :- | :- |
| `Invalid metastoreUri; expected thrift://host:port` | URI is not `thrift://host:port`, or has a path, credentials or query | Use `thrift://host:port`. For TLS, set `metastore.tls_enabled = 'true'` |
| `CREATE TABLE ... WITH (type = '...') requires the 'catalog_uri' option.` | A required option is missing (also for `schema_name`, `storage.sas_token`, `storage.credential_prefix`) | Add the named option |
| `Hive Metastore table format does not match type = '...'.` | `type` doesn't match the HMS table | Use `iceberg` for Iceberg tables and `hive` for Parquet tables |
| `type = 'hive' requires catalog_type = 'hive'.` | `type = 'hive'` with another `catalog_type` | Remove `catalog_type` or set it to `hive` |
| `REST auth options are not supported for Hive Metastore.` | `auth_type`, `token`, `client_id`, `client_secret`, `oauth_scope` or `oauth_token_uri` was set | Remove them. Use `metastore.*` options |
| `metastore.* options require catalog_type = 'hive'.` | `metastore.*` on a REST catalog | Set `catalog_type = 'hive'` or remove them |
| `Native Hive Parquet tables support only snapshot_mode = 'latest'.` or `Native Hive Parquet tables support only the latest unversioned listing` | `snapshot_mode` or `snapshot_id` on a native table | Remove `snapshot_mode` and `snapshot_id`, or set `latest` |
| `Transactional Hive tables are not supported as native Parquet external tables` | The HMS table is ACID | Use a non-transactional Parquet table |
| `Only native Hive Parquet tables are supported; inputFormat=...` | The table or a partition is not Parquet (ORC, text, ...) | Use a Parquet table |
| `No Parquet files found for native Hive table <db>.<table>; schema inference requires a data file` | The table is empty, or its files can't be listed | Add a file, and check partition locations and list permissions |
| `Hive external tables currently require s3:// or s3a:// storage without URI credentials or query parameters` | Unsupported location scheme | Use S3, or Azure with `storage.provider = 'azure'` |
| `Native Hive Azure storage requires an explicit sourceStorageProvider` | Native table on Azure without `storage.provider` | Set `storage.provider = 'azure'` with `storage.sas_token` and `storage.credential_prefix` |
| `Azure SAS options require storage.provider = 'azure'.` | `storage.sas_token` or `storage.credential_prefix` without Azure | Set `storage.provider = 'azure'` |
| `Option 'storage.region' is incompatible with Azure SAS storage.` | An S3 option with Azure (also `access_key`, `secret_key`, `session_token`, `endpoint`) | Remove the S3 options |
| `Unsupported storage.provider: '...'.` | Unknown storage provider | Use `s3` or `azure` |
| `metastoreAuthType must be none, kerberos, or delegation-token` | Bad `metastore.auth_type` | Use one of the listed values |
| `metastorePrincipal must include an explicit realm: user[/instance]@REALM` | Client principal has no realm | Add `@REALM` |
| `metastoreServerPrincipal must be service/host@REALM` | Server principal is malformed | Use for example `hive/_HOST@EXAMPLE.COM` |
| `metastoreServerPrincipal is required for the selected metastore security mode` | Kerberos without `server_principal` (or `principal`) | Add the missing option |
| `Configure either metastoreKeytab or metastoreTicketCache` | Both keytab and ticket cache set | Keep one |
| `metastoreKeytab is incompatible with the selected metastore security mode` | A Kerberos, token or `sasl_qop` option set for a mode that doesn't use it | Remove options that don't belong to your `auth_type` |
| `metastoreTruststorePath is incompatible with the selected metastore security mode` | TLS store options without TLS | Set `metastore.tls_enabled = 'true'` or remove them |
| `Hive TLS with SASL requires metastoreSaslQop=auth` | Kerberos or token with TLS and `sasl_qop` other than `auth` | Set `metastore.sasl_qop = 'auth'` |
| `SASL cannot be combined with metastoreFramedTransport` | `framed_transport = 'true'` with Kerberos or a token | Remove `metastore.framed_transport` |
| `metastoreTlsProtocols supports TLSv1.2 and TLSv1.3` | Unsupported TLS version | Use `TLSv1.2`, `TLSv1.3` or both |
| `Invalid metastoreDelegationToken` | Token is not a URL-safe Base64 Hadoop token | Re-encode with `Token.encodeToUrlString()` |
| `Hive Metastore Kerberos login failed` | Principal doesn't match the keytab or cache, a file is missing, or the realm or KDC is wrong | Check the principal and keytab; contact StarTree support to check the Kerberos configuration |
| `Hive Metastore TLS connection failed` | Untrusted certificate, hostname mismatch, or wrong store type or password | Check the truststore, the certificate SAN and `truststore_type` |
| `Hive Metastore SASL authentication failed` | Server principal or QOP doesn't match the server | Check `server_principal` and match `sasl_qop` to the server |
| `... requires a resolved secret reference` | A `srn2:secret#` reference could not be resolved | Check the secret name, and use the task config key as the secret's data key |
| `Catalog plugin is unavailable: ...` | The Hive Metastore catalog is not enabled in your environment | Contact StarTree support |
| `Hive Metastore catalog does not support enabled=true periodic watcher mode; omit 'enabled' for controller snapshot sync` | `enabled` set in the sync task config | Remove `enabled` |
| `Hive Metastore catalog requires executor=controller` | Task config edited to another executor | Set `executor` to `controller` |
| `Native Hive Parquet tables do not support segment groups; disable segmentGroupConfig` | `segmentGroupConfig` enabled on a native table | Remove `segmentGroupConfig` or set `"enabled": false` |
| `enableSnapshotConsistency=true but the catalog does not support snapshotting; fix the config or remove the flag` | Flag set to `true` on a native table | Remove the flag |
| `Native Hive bounded string data columns are unsupported: <column> (...)` | A `char(n)` or `varchar(n)` data column | Declare it as `string` in HMS |
| `Unsupported Hive column type '...' for <column>` | `array`, `map`, `struct` or another unsupported type | Remove the column or use a supported type |
| `Hive column '<column>' differs in case from Parquet column '<name>'` | Column names differ in case | Make the HMS and Parquet names match exactly |

## What's next

1. **Add indexes for your query patterns.** → [Indexes](https://docs.startree.ai/corecapabilities/external-table/indexes.md)
2. **Turn on caching and pin hot indexes.** → [Data and Index Caching](https://docs.startree.ai/corecapabilities/external-table/data-and-index-caching.md)
3. **Watch the scheduled syncs.** → [Observability](https://docs.startree.ai/corecapabilities/external-table/observability.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.