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

  1. Inspect pg_publication_tables directly on the server to confirm what the catalog really contains
  2. Restore catalog integrity (clean backup restore or recovery via support channels)
  3. Test against vanilla PostgreSQL of a supported version to rule out shim/proxy interference
  4. Drop and recreate the publication if only its metadata is corrupted
  5. 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

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


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/e8a9587077601491. Report an issue: GitHub.