Problem
information_schema.columns is rewritten to main.information_schema_columns_compat (transpiler/transform/information_schema.go), and all three definitions of that view declare the UDT columns as bare, untyped NULL:
| Definition |
udt_schema |
udt_name |
server/catalog.go:1580 (standalone) |
NULL (:1640) |
NULL (:1641) |
server/catalog.go:1667 (DuckLake mode) |
NULL (:1727) |
NULL (:1728) |
server/sessionmeta/sessionmeta.go:744 (session) |
NULL (:884) |
c.udt_name (:885) |
A bare NULL column resolves to INTEGER in DuckDB's catalog — DESCRIBE reports it as INTEGER, and that is the type a client sees over the wire. Any client that compares udt_schema to a string therefore fails, because the string literal gets coerced toward INT32 rather than the column being coerced toward text.
Metabase's Postgres driver does exactly that. Its column-sync query (src/metabase/driver/postgres.clj:339) schema-qualifies enum types with:
CASE WHEN c.udt_schema IN ('public', 'pg_catalog')
THEN format('%s', c.udt_name)
ELSE format('"%s"."%s"', c.udt_schema, c.udt_name)
END AS "database-type"
which comes back as:
ERROR: rpc error: code = Unknown desc = Conversion Error: Could not convert string 'public' to INT32
LINE 1: SELECT c.column_name AS name, CASE WHEN c.udt_schema IN ('public', 'pg_catalog') THEN COALESCE(c.udt_name::"varchar...
^
The caret is on the 'public' literal in the udt_schema IN list.
(The COALESCE(...::"varchar", '') in the echoed statement is the transpiler's own format('%s', …) rewrite from transpiler/transform/functions_compat.go:109, not what the client sent — noting it so the echo isn't mistaken for a client-side oddity.)
Impact
Metabase issues one describe-fields statement per schema covering every table at once, so a single failure means zero columns sync for the entire database. Tables show up with row counts, but every question against them fails with Table 'X' has no Fields associated with it. The database looks connected and half-populated rather than broken, which makes it hard to diagnose from the client side.
More generally this hits any client that filters or joins on udt_schema — a common pattern for resolving user-defined and enum types.
Reproduction
Client side: point Metabase (v1.63.2 / OSS v0.63.2, Postgres driver) at a duckgres endpoint, add it as a database, and let sync run. Field sync fails for every table; the error above appears in the Metabase logs from metabase.sync.util.
Minimal, engine-side — an INTEGER-typed udt_schema reproduces the error text and caret position exactly (DuckDB 1.5.5):
CREATE TABLE cols AS SELECT 'x' AS column_name, NULL AS udt_schema;
DESCRIBE cols; -- udt_schema INTEGER
SELECT c.column_name FROM cols c WHERE c.udt_schema IN ('public','pg_catalog');
-- Conversion Error: Could not convert string 'public' to INT32
One thing I could not pin down from outside: a plain DuckDB view over NULL AS udt_schema keeps the column as SQLNULL at runtime and tolerates the comparison, even though DESCRIBE reports INTEGER. The failure needs the column to be genuinely INTEGER-typed by the time the predicate binds. So the type is hardening somewhere in the duckgres path — worker dispatch, the compat-view rebind, or the pgwire type resolution — and it would be worth confirming where before fixing, in case other compat columns are affected the same way. DESCRIBE main.information_schema_columns_compat on a live session should show it.
Notes on the fix
The narrow fix is a typed NULL in all three definitions:
NULL::VARCHAR AS udt_catalog,
NULL::VARCHAR AS udt_schema,
The idiom is already used elsewhere in the same files — CAST(NULL AS VARCHAR) AS type_udt_name at server/catalog.go:1905, NULL::INTEGER AS cache_size at server/sessionmeta/sessionmeta.go:731 — so not sure if this was intentional or unintentional difference for udt_catalog and udt_schema.
Two additional items you're there:
-
Might be worth an audit of the other bare NULL AS <col> columns in the compat views. Under the SQL standard, most information_schema.columns columns are sql_identifier / character_data, i.e. text. Every one currently emitted as an untyped NULL is a latent version of this bug for any client that filters on it.
-
udt_name diverges between the definitions. sessionmeta.go:885 returns the real c.udt_name, but both catalog.go definitions return NULL. Even after the type fix, a client on those paths gets a NULL udt_name for every column — so anything deriving a column's type from udt_name still comes back empty.
Found while investigating a Metabase instance syncing against a hosted PostHog warehouse endpoint. Source references are against main as of 2026-07-30; error text and caret position reproduced locally against DuckDB 1.5.5.
Problem
information_schema.columnsis rewritten tomain.information_schema_columns_compat(transpiler/transform/information_schema.go), and all three definitions of that view declare the UDT columns as bare, untypedNULL:udt_schemaudt_nameserver/catalog.go:1580(standalone)NULL(:1640)NULL(:1641)server/catalog.go:1667(DuckLake mode)NULL(:1727)NULL(:1728)server/sessionmeta/sessionmeta.go:744(session)NULL(:884)c.udt_name(:885)A bare
NULLcolumn resolves toINTEGERin DuckDB's catalog —DESCRIBEreports it asINTEGER, and that is the type a client sees over the wire. Any client that comparesudt_schemato a string therefore fails, because the string literal gets coerced toward INT32 rather than the column being coerced toward text.Metabase's Postgres driver does exactly that. Its column-sync query (
src/metabase/driver/postgres.clj:339) schema-qualifies enum types with:which comes back as:
The caret is on the
'public'literal in theudt_schemaINlist.(The
COALESCE(...::"varchar", '')in the echoed statement is the transpiler's ownformat('%s', …)rewrite fromtranspiler/transform/functions_compat.go:109, not what the client sent — noting it so the echo isn't mistaken for a client-side oddity.)Impact
Metabase issues one
describe-fieldsstatement per schema covering every table at once, so a single failure means zero columns sync for the entire database. Tables show up with row counts, but every question against them fails withTable 'X' has no Fields associated with it. The database looks connected and half-populated rather than broken, which makes it hard to diagnose from the client side.More generally this hits any client that filters or joins on
udt_schema— a common pattern for resolving user-defined and enum types.Reproduction
Client side: point Metabase (v1.63.2 / OSS v0.63.2, Postgres driver) at a duckgres endpoint, add it as a database, and let sync run. Field sync fails for every table; the error above appears in the Metabase logs from
metabase.sync.util.Minimal, engine-side — an INTEGER-typed
udt_schemareproduces the error text and caret position exactly (DuckDB 1.5.5):One thing I could not pin down from outside: a plain DuckDB view over
NULL AS udt_schemakeeps the column asSQLNULLat runtime and tolerates the comparison, even thoughDESCRIBEreportsINTEGER. The failure needs the column to be genuinely INTEGER-typed by the time the predicate binds. So the type is hardening somewhere in the duckgres path — worker dispatch, the compat-view rebind, or the pgwire type resolution — and it would be worth confirming where before fixing, in case other compat columns are affected the same way.DESCRIBE main.information_schema_columns_compaton a live session should show it.Notes on the fix
The narrow fix is a typed NULL in all three definitions:
The idiom is already used elsewhere in the same files —
CAST(NULL AS VARCHAR) AS type_udt_nameatserver/catalog.go:1905,NULL::INTEGER AS cache_sizeatserver/sessionmeta/sessionmeta.go:731— so not sure if this was intentional or unintentional difference forudt_catalogandudt_schema.Two additional items you're there:
Might be worth an audit of the other bare
NULL AS <col>columns in the compat views. Under the SQL standard, mostinformation_schema.columnscolumns aresql_identifier/character_data, i.e. text. Every one currently emitted as an untypedNULLis a latent version of this bug for any client that filters on it.udt_namediverges between the definitions.sessionmeta.go:885returns the realc.udt_name, but bothcatalog.godefinitions returnNULL. Even after the type fix, a client on those paths gets a NULLudt_namefor every column — so anything deriving a column's type fromudt_namestill comes back empty.Found while investigating a Metabase instance syncing against a hosted PostHog warehouse endpoint. Source references are against
mainas of 2026-07-30; error text and caret position reproduced locally against DuckDB 1.5.5.