wherobots-dbt) makes Wherobots Cloud a target in a dbt project. You write models as Spatial SQL files. dbt builds them in dependency order and Wherobots runs them against your catalogs. Your spatial models are then version-controlled, tested, and documented like the rest of your dbt project.
Benefits
Spatial models work like any dbt model
Spatial models work like any dbt model
ref() dependencies, and lineage, selectors, and scheduling work as they do in any dbt project. Each dbt invocation runs against one target, so if the same project also builds models on another warehouse, build the Wherobots models and the warehouse models in separate runs, each with its own target and a selector. The Wherobots models can read only catalogs your organization can access, so any warehouse output they depend on has to be in one of those catalogs. See Read from other catalogs.Tests and documentation work as usual
Tests and documentation work as usual
unique, not_null, relationships, accepted_values), singular tests, and dbt docs generate all work against Wherobots relations. You can assert spatial invariants, such as a consistent spatial reference identifier (SRID), with plain SQL tests.Iceberg tables by default
Iceberg tables by default
table, incremental, and snapshot — materializes as an Apache Iceberg table, so you get schema evolution, partitioning, and time travel without configuring a file format. A view model stays a view, and an ephemeral model is never persisted at all.WherobotsDB stores tables in Apache Iceberg. New tables in Wherobots-managed catalogs, such as org_catalog, are format-version 3 (V3), which has geometry and geography column types, and Wherobots adds a raster column type. Tables created before September 15, 2026 were created at format-version 2 (V2), and Wherobots doesn’t raise them to V3. Which column types a table can have, and whether WherobotsDB skips data files for spatial filters, depend on its format version; see the Iceberg Tables Overview.Geometry is a column type
Geometry is a column type
Before you start
Wherobots requirements
Wherobots requirements
- A Wherobots Account. See Create a Wherobots Account.
- A Wherobots API key. See API keys.
- A catalog you can write to. The adapter has no implicit default catalog, so you must name one in your profile. See Data Catalogs.
dbt requirements
dbt requirements
- Python 3.10 or later.
- dbt Core 1.9 or later (below 2.0).
- Familiarity with dbt projects, profiles, and models. See the dbt documentation.
Install the adapter
Install the adapter from PyPI into the same Python environment as dbt:dbt.adapters.wherobots, so any profiles.yml target with type: wherobots resolves automatically with no further configuration.
Configure your profile
Add a Wherobots target to profiles.yml
type: wherobots to ~/.dbt/profiles.yml. Read the API key from an environment variable so no secret is written to disk.catalog is the Wherobots name for dbt’s database. Use one or the other, never both — dbt rejects a target that sets both keys.Export your API key
Point your project at the profile
profile: in dbt_project.yml to the profile name you defined above.Verify the connection
dbt debug from your project directory.Connection test: [OK connection ok], along with the resolved host, port, database, schema, threads, retries, runtime, and region. Your API token is not included in this output.The first connection provisions Wherobots compute, which can take several minutes. dbt logs provisioning heartbeats while it waits, so you can tell the run is not stuck.Profile reference
The following keys are valid in atype: wherobots output.
Required
Optional
Sessions and billing
dbt runs every statement on a Wherobots SQL session, which runs on a runtime. The adapter attaches to a session at a run’s first statement and reuses the run’s connections for later models, so the number of connections doesn’t grow with the number of models. The adapter attaches another connection only when every open one is busy. With the defaultsession_type and force_new, all of a run’s connections land on one session. dbt closes the run’s connections when it exits.
Billing for the session starts when the session is ready, not while its runtime starts. See What starts a billable event. After the session’s last connection closes, the session keeps running, and billing, until it stops. If no other run uses the session, its last connection closes when dbt exits.
- The session’s idle timeout is
shutdown_after_inactive_seconds, counted from when its last connection closes, whichever run that connection belongs to. The default is about 5 minutes. - Wherobots checks for idle sessions periodically, so the session stops up to about 3 minutes after the timeout runs out.
- A session also stops when it reaches your organization’s Runtime TTL.
force_new: false that starts before the session stops attaches to it in seconds instead of waiting for a new runtime.
force_new: true starts a new runtime for every model.When runs share a session
A connection withforce_new: false attaches to a running session when all of the following match:
- The Wherobots user that the API key belongs to.
region.runtimeandsession_type, after defaults are filled in. An unsetsession_typematchesmulti, and an unsetruntimematches your organization’s default runtime.shutdown_after_inactive_seconds, as written. An unset value matches only another unset value, not an explicit number, even one equal to the server default.
threads don’t affect the match. Two unrelated projects that use the same API key, runtime, and region share one session, and with the default session_type: multi they share it while both run. How a session started doesn’t affect the match either: a session that a force_new: true connection started accepts any matching connection that comes after it.
Runs on one session share its compute, its Spark configuration (anything a set statement changes), and its temporary views. The adapter derives each staging view’s name from the catalog, schema, and name of the table it builds, and sets the partition overwrite mode on each statement, so its statements from runs that build different tables don’t collide. Two runs that build the same table at the same time use the same staging view and write the same table, so don’t run them concurrently. That includes runs that use different dbt targets with the same catalog and schema. A hook or model of your own that runs set or creates a temporary view affects every run on the session.
Keep runs apart
Usesession_type: single. Runs that overlap in time get separate runtimes, and a run with threads above 1 can hold up to threads + 1 runtimes. After a run exits, a later run with the same settings still attaches to its session until the session stops.
force_new: true doesn’t keep runs apart. Each connection the adapter opens waits for a new runtime instead of attaching to a running one, so one run can start up to threads + 1 runtimes. With session_type: multi, another run with matching settings and force_new: false can still attach to those sessions, while the forcing run is connected or after it exits.
Shorten the idle time of scheduled runs
For a job that runs once a day, a session left running after the job finishes only adds cost. Lowershutdown_after_inactive_seconds:
dbt seed and then dbt build as separate Airflow tasks, set the timeout longer than the longest gap between them. Each command then attaches to the running session instead of waiting for a new runtime.
Read from other catalogs
The profile’scatalog and schema control where your models are written. Inputs are separate: a source’s database: key names the catalog that source reads from, so one project can read Wherobots-hosted catalogs and write to your own.
bbox columns keeps the scan to New York City instead of every place on the planet:
wherobots_open_data and wherobots_pro_data are available to every Organization Edition. See Spatial catalog. If a source fails to resolve, see Troubleshooting.
What works today
Limitations
Query cancellation is not supported
Query cancellation is not supported
dbt run stops the dbt process but does not stop work already running on the server. Let a long query finish, or stop the runtime from the Wherobots Cloud interface.Only token authentication is supported
Only token authentication is supported
authenticator must be token and api_token must be set. Single sign-on and OAuth are not available through the adapter.on_schema_change='sync_all_columns' is unsupported
on_schema_change='sync_all_columns' is unsupported
alter column ... type, which the Iceberg catalog rejects. Use append_new_columns, fail, or ignore.Non-string seed column_types overrides fail
Non-string seed column_types overrides fail
+column_types: {birthday: date} fails at analysis time. Declare the column as string and cast it in a downstream model instead. The geometry override is the exception — it is handled by the geometry type handler. See Spatial data in dbt models.Seeds cannot declare a geography column
Seeds cannot declare a geography column
+column_types: {g: geography} fails with the engine’s UNSUPPORTED_DATATYPE error, because a bare geography column declares no coordinate reference system. Models can still materialize geography columns. See Geography.Model contracts are not enforced
Model contracts are not enforced
contract: {enforced: true} builds as a plain create table as select. The adapter does not check the query’s columns or types against the contract.Troubleshooting
Wherobots profile is missing 'catalog' (dbt 'database')
Wherobots profile is missing 'catalog' (dbt 'database')
catalog nor database set. The adapter has no implicit default catalog.Solution:- Add
catalog: <a-catalog-you-can-write-to>to the output inprofiles.yml. - Do not set both
cataloganddatabase— dbt rejects the target before validation runs.
dbt run appears to hang on the first model
dbt run appears to hang on the first model
- Watch for
provisioningheartbeats in the dbt log — they confirm the attach is progressing. - Leave
force_newatfalse, so the run attaches to a running session when one matches. See When runs share a session. - If you run dbt commands back to back, set
shutdown_after_inactive_secondslonger than the gap between them. The session keeps running, and billing, for that long after each command.
Runtime Error: SQL connection lost
Runtime Error: SQL connection lost
SQL connection lost (session=…, execution=…). Commit outcome is unknown; verify the operation before retrying writes. The adapter does not retry a statement that may have reached the server.Solution: Run the failed models again, after checking what a second run writes. The connection can drop during any statement of a model, including a hook, and that statement may have committed:- A re-run runs every hook again, so a hook that writes data writes it a second time if the failed attempt already ran it.
- A view, table, seed, or snapshot ends with the same rows, and so does an incremental model that uses
insert_overwrite. - An incremental model that uses
mergeordelete+insertends with the same rows, except for rows a re-run inserts a second time:- every row, when the model has no
unique_key - a row whose
unique_keyisNULL(see merge) - a row whose existing copy falls outside the model’s
incremental_predicates
- every row, when the model has no
- A
delete+insertmodel deletes and then inserts in two statements. If the connection dropped after the delete committed, a re-run restores the deleted rows only if the model still selects them. Anis_incremental()filter that keeps only keys already in the table does not, and those rows stay missing. - An
appendmodel inserts every row a second time, unless itsis_incremental()filter excludes rows already in the table.
TABLE_OR_VIEW_NOT_FOUND on a source
TABLE_OR_VIEW_NOT_FOUND on a source
database: names a catalog your organization cannot read, or the relation does not exist in that catalog.Solution:- Confirm the catalog is attached to your organization in Data Catalogs.
wherobots_open_dataandwherobots_pro_dataare available to every Organization Edition. If either is missing from Data Catalogs, contact Wherobots Support.
MERGE_CARDINALITY_VIOLATION during an incremental run
MERGE_CARDINALITY_VIOLATION during an incremental run
unique_key value that already exists in the table. dbt’s unique_key contract requires the key to be unique in the model’s output. The table is left unchanged.Solution:- Deduplicate the model’s select so each
unique_keyvalue appears once. - Add a
uniquetest on the key. The run fails only when a duplicated key already exists in the table. Duplicates of a new key are inserted with no error, and the test is what catches them.

