materialize-bigquery-create · git:20260505.8873932 · 2026-05-05 · sha256 4a4be67663377a2b
materialize-bigquery-create git:20260505.8873932A
Immutable. This exact content is served forever at /api/v1/blob/4a4be67663377a2b.
---
name: materialize-bigquery-create
description: Create a BigQuery materialization to stream Estuary collections into BigQuery tables. Use when setting up BigQuery as a destination for captured data. Use when user says "send to BigQuery", "load into BigQuery", "materialize to BigQuery", or "BigQuery destination".
---
# Create BigQuery Materialization
Create a BigQuery materialization using flowctl to stream data from Estuary collections into BigQuery tables.
**Applies to**: materialize-bigquery
## Step 0: Load Connector Documentation
Before proceeding, fetch the official connector docs for prerequisites, config reference, and setup instructions.
**Load the docs page:**
https://docs.estuary.dev/reference/Connectors/materialization-connectors/BigQuery/
Use WebFetch to load this page. It covers:
- Prerequisites (GCS bucket, service account, IAM roles, dataset)
- Full config property reference
- gcloud CLI commands for setup
- Advanced options (delta updates, hard deletes, sync schedule)
**Search Kapa for tribal knowledge** (if the Estuary MCP is configured):
```
Search kapa ai knowledge sources for "materialize bigquery common issues"
```
If Kapa MCP is not configured, the user can set it up: https://docs.estuary.dev/features/mcp-integration/
This skill provides the **flowctl workflow** and **troubleshooting** that docs don't cover.
## Step 1: Gather Requirements
Before writing any YAML, ask the user:
1. **GCP project ID?** — The Google Cloud project
2. **BigQuery dataset and region?** — Target dataset name and GCP region
3. **GCS staging bucket?** — Bucket name for staging data (**must be same region as dataset!**)
4. **Authentication method?** — Service account key (most common) or GCP IAM (workload identity federation)
5. **Non-default data plane?** — Most users use the default. Ask if they need a non-default data plane.
6. **Source collections?** — Which Estuary collections to materialize
7. **Hard deletes?** — Off by default. Without it, deleted rows stay in BigQuery marked with `_meta/op: 'd'`. Enable to physically remove them.
8. **Delta updates?** — Off by default. Switches from one-row-per-key (standard merge) to append-only. Use for event logs or history tables.
9. **Sync schedule?** — Controls how often batches are written to BigQuery (default: 30 minutes, `0s` for real-time). Affects latency and compute cost.
**CRITICAL**: The GCS staging bucket and BigQuery dataset must be in the same GCP region. Region mismatch is the most common setup error.
## Step 2: Find the Correct Connector Version
Always use the latest numbered version tag. Query the connector registry:
```bash
flowctl raw get --table connector_tags \
--query 'documentation_url=eq.https://go.estuary.dev/materialize-bigquery' \
--query 'select=image_tag,documentation_url' \
--output yaml
```
Use the returned `image_tag` — never hardcode a version.
## Step 3: Help User Complete Prerequisites
Walk the user through prerequisites from the docs loaded in Step 0:
1. **GCS staging bucket** — Create in the same region as the BigQuery dataset
2. **Service account** — Create with required IAM roles:
- `bigquery.dataEditor` — create/update tables
- `bigquery.jobUser` — run BigQuery jobs
- `bigquery.readSessionUser` — read session access
- `storage.objectAdmin` — read/write staging files in GCS bucket
3. **BigQuery dataset** — Create if it doesn't exist
4. **Service account key** — Generate JSON key file
Refer to the docs page for exact gcloud commands.
## Step 4: Create the Spec File
Build `flow.yaml` using the config reference from the docs. Minimal required config:
```yaml
materializations:
<TENANT>/<PATH>/materialize-bigquery:
endpoint:
connector:
image: ghcr.io/estuary/materialize-bigquery:<VERSION>
config:
project_id: "<GCP-PROJECT-ID>"
dataset: "<BIGQUERY-DATASET>"
bucket: "<GCS-BUCKET-NAME>"
bucket_path: "estuary-staging"
region: "<REGION>"
# Option A: Service account key (most common)
credentials_json: |
{
...full service account key JSON...
}
# Option B: GCP IAM (workload identity federation)
# credentials:
# auth_type: "GCPIAM"
# gcp_service_account_to_impersonate: "<SERVICE-ACCOUNT-EMAIL>"
# gcp_workload_identity_pool_audience: "<WORKLOAD-IDENTITY-POOL-AUDIENCE>"
bindings:
- source: <TENANT>/<collection-path>
resource:
table: "<TABLE_NAME>"
dataset: "<OPTIONAL-DATASET-OVERRIDE>"
```
**Important**: `bucket` is just the bucket name — no `gs://` prefix. `region` must match both bucket and dataset.
## Step 5: Publish
```bash
flowctl catalog publish --source flow.yaml --auto-approve
```
## Step 6: Verify
```bash
# Check status
flowctl catalog status <TENANT>/<PATH>/materialize-bigquery
# View logs
flowctl logs --task <TENANT>/<PATH>/materialize-bigquery --since 5m | jq -c '{ts, message}'
```
**Status progression:**
1. `PENDING` — Normal for ~30 seconds during shard assignment
2. `BACKFILLING` — Initial data sync from collections
3. `OK` — Running normally with real-time updates
## Troubleshooting
### "no such bucket" or "bucket does not exist"
**Cause**: GCS bucket doesn't exist or name is incorrect
**Fix**: Create the bucket first — see docs for gcloud command. Ensure no `gs://` prefix in config.
### "not authorized to write to bucket" or storage access denied
**Cause**: Service account lacks permissions on the GCS bucket
**Fix**: Grant `roles/storage.objectAdmin` on the bucket — see docs for gcloud command.
### "Error 409: Already Exists: Dataset"
**Cause**: Service account can't see the existing dataset due to missing permissions, so it tries to create it
**Fix**: This paradoxical error means the dataset exists but the service account can't list it. Grant `bigquery.dataEditor` and `bigquery.jobUser` at the project level.
### "accessDenied" or "403 Forbidden" on BigQuery
**Cause**: Missing BigQuery IAM roles
**Fix**: Ensure all three roles are granted: `bigquery.dataEditor`, `bigquery.jobUser`, `bigquery.readSessionUser`. See docs for gcloud commands.
### "Invalid credentials" or authentication failures
**Cause**: Service account key is invalid, incomplete, or malformed
**Fix**:
1. Regenerate the key
2. Ensure entire JSON content is included (including `{` and `}`)
3. Use YAML `|` for multiline credentials_json
### "BucketRegionError" or 301 status
**Cause**: GCS bucket is in a different region than the BigQuery dataset
**Fix**: Both must be in the same region. Check with gcloud — see docs. Recreate whichever is easier to move.
### Staging files accumulating in bucket
**Cause**: Materialization failed during "store" phase before cleanup
**Fix**: The connector normally cleans up staging files. If files accumulate, check logs for errors, fix the underlying issue, then manually delete old staging files.
### "jobInternalError" or "Error 400" from BigQuery
**Cause**: Transient BigQuery backend error
**Fix**: Estuary automatically retries these. If persistent, check BigQuery service status and logs for specific error details.
### Materialization stuck in PENDING
Wait 30-60 seconds — this is normal during shard assignment. If still stuck:
```bash
flowctl logs --task <TENANT>/<PATH>/materialize-bigquery --since 5m | jq 'select(.level == "error")'
```
## Related Skills
- `connector-disable-enable` — Pause/restart existing materializations
- `connector-delete-recreate` — Nuclear option for stuck materializations
- `estuary-logs` — Deep log analysis
- `estuary-catalog-status` — Status checking