VARCHAR). An event is never rejected because a field changed type.
Typing happens in a downstream clean step — a small SQL node that casts each column to the type you want. This is the medallion pattern: a raw layer that accepts everything, and a typed layer you build on top.
This applies to event sources only. File uploads and database connectors keep their real column types — see the table below.
Why event fields are text
Panels infers a column’s type from the first event it sees, and a stored column’s type is immutable. If the first event happened to make a field look like a specific type, a later event that doesn’t match would fail forever:- The first
referrerarrives as an object{"url": "..."}→ the column locks to a structured type → a later plain-stringreferrercan’t be stored. - The first
device_idarrives as550e8400-e29b-41d4-a716-446655440000→ it looks like a UUID and locks the column to UUID → a laterdevice_idof"unknown"can’t be stored.
Which sources store text
| Source | How fields are stored |
|---|---|
| Push / webhook (HTTP ingest) | Every field as text |
| Email (JSON body payload) | Every field as text |
| API / app connectors | Scalars keep their inferred type; nested fields (objects, arrays) stored as JSON text |
| File upload (CSV, Parquet, JSON) | Real column types (the file’s schema) |
| Database / warehouse (PostgreSQL, Snowflake, …) | Real column types (the source catalog) |
| Email attachments (CSV, etc.) | Real column types (the file’s schema) |
Querying text columns
Text columns are fully queryable. Two functions do most of the work:try_cast(col AS TYPE)— convert a value, returningNULLif it can’t (unlikecast, which errors). This is what makes typing safe.json_extract_string(col, '$.path')— pull a value out of a field that holds JSON text.
Create a typed table
Add a SQL node downstream of your event source that casts each column. By convention, name itclean_events — your raw source stays the bronze layer, and this becomes the typed silver layer your dashboards build on.
1
Add a SQL node
Connect a new SQL node to your event source.
2
Cast each column with try_cast
Convert every field you care about to its real type. Anything that can’t be cast becomes
NULL instead of breaking the run.3
Build on the typed table
Point charts, dashboards, and further transforms at
clean_events. They get clean types; ingestion stays bulletproof.