windmill-labs/windmill · error
Unexpected NULL `table_name` for schema `{}` in publication
Error message
Unexpected NULL `table_name` for schema `{}` in publication `{}`. This should never happen unless PostgreSQL internals are corrupted. What it means
Same catalog-integrity guard as its sibling: when reading publication relations, a NULL table_name for a known schema inside a publication is treated as impossible in a healthy PostgreSQL and aborts with this error naming the schema and publication.
Source
Thrown at backend/windmill-trigger-postgres/src/handler.rs:942
let mut table_to_track: HashMap<String, Relations> = HashMap::new();
for row in rows {
let schema_name: Option<String> = row.get("schema_name");
let table_name: Option<String> = row.get("table_name");
let columns: Option<Vec<String>> = row.get("columns");
let where_clause: Option<String> = row.get("where_clause");
let schema_name = schema_name.ok_or_else::<Error, _>(|| {
anyhow::anyhow!(
"Unexpected NULL `schema_name` in publication entry (pubname: `{}`). This should never happen unless PostgreSQL internals are corrupted.",
publication_name,
)
.into()
})?;
let table_name = table_name.ok_or_else::<Error, _>(|| {
anyhow::anyhow!(
"Unexpected NULL `table_name` for schema `{}` in publication `{}`. This should never happen unless PostgreSQL internals are corrupted.",
schema_name,
publication_name,
)
.into()
})?;
let entry = table_to_track.entry(schema_name.clone());
let table_to_track_item = TableToTrack::new(table_name, where_clause, columns);
match entry {
std::collections::hash_map::Entry::Occupied(mut occupied) => {
occupied.get_mut().add_new_table(table_to_track_item);
}
std::collections::hash_map::Entry::Vacant(vacant) => {
vacant.insert(Relations::new(schema_name, vec![table_to_track_item]));
}
}View on GitHub (pinned to e474e8803c)
Solutions
- Inspect pg_publication_tables directly on the server to confirm what the catalog really contains
- Restore catalog integrity (clean backup restore or recovery via support channels)
- Test against vanilla PostgreSQL of a supported version to rule out shim/proxy interference
- Drop and recreate the publication if only its metadata is corrupted
- File a Windmill issue with Postgres version and query output if it reproduces on a healthy DB
Defensive patterns
Strategy: try-catch
Validate before calling
-- detect NULL table names in the publication before use SELECT pubname, schemaname, tablename FROM pg_publication_tables WHERE pubname = 'my_publication' AND tablename IS NULL; -- must return zero rows
Try / catch
if let Err(e) = get_publication_info(...).await {
if e.to_string().contains("Unexpected NULL `table_name`") {
// recreate the publication metadata instead of retrying
sqlx::query("DROP PUBLICATION my_publication").execute(&mut *tx).await?;
sqlx::query("CREATE PUBLICATION my_publication FOR TABLE ...").execute(&mut *tx).await?;
}
return Err(e.into());
} Prevention
- Recreate the publication if its metadata rows look NULL/corrupt
- Compare behavior on vanilla PostgreSQL to rule out server shims
- Check the publication after major Postgres version upgrades
- Pin to Postgres versions in Windmill's supported matrix
When it happens
Trigger: The publication-info query returns a row where table_name is NULL for the given schema in publication {pubname} — indicating corrupted pg catalog data or a non-standard server answering the query.
Common situations: Corrupted system catalogs after failed upgrades/restores; third-party Postgres-compatible servers (proxies, shims) returning NULL columns; version mismatches between the driver's query assumptions and the server.
Related errors
- Unexpected NULL `schema_name` in publication entry (pubname:
- could not read the run of job ${id}: ${res.status} ${await r
- error (from getWorkspaceMutationTargetError)
- Failed to create ${triggerConfig.label} "${requestBody.path}
- Unknown CaptureTriggerKind: ${kind}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/e8a9587077601491.
Report an issue: GitHub.