windmill-labs/windmill · error

34000

34000

Error message

portal ${formatProtocolName(describe.name, "portal")} does not exist

What it means

PostgreSQL 'invalid_cursor_name' (34000) error: a Describe message targeting type 'P' (portal) referenced a portal name not present in the portals map. Portals are created by Bind and live only on the current connection, so describing an unknown portal is a protocol-level error.

Source

Thrown at cli/src/commands/datatable/serve.ts:506

      isQueryLikelySafeToDescribe(statement.query)
    ) {
      const envelope = await executeQuery(statement.query);
      return concat([
        buildParameterDescription(statement.parameterTypeOids),
        envelope.columns.length > 0
          ? buildRowDescription(envelope.columns)
          : buildNoData(),
      ]);
    }
    return concat([
      buildParameterDescription(statement.parameterTypeOids),
      buildNoData(),
    ]);
  }

  const portal = portals.get(describe.name);
  if (!portal) {
    throw createPgProtocolError(
      `portal ${formatProtocolName(describe.name, "portal")} does not exist`,
      "34000",
    );
  }

  if (!portal.envelope && isQueryLikelySafeToDescribe(portal.query)) {
    portal.envelope = await executeQuery(buildPortalQuery(portal));
  }
  if (portal.envelope?.columns.length) {
    portal.sentRowDescription = true;
    return buildRowDescription(portal.envelope.columns);
  }
  return buildNoData();
}

async function handleExecuteMessage(
  data: Uint8Array,
  portals: Map<string, PortalState>,

View on GitHub (pinned to e474e8803c)

Solutions

  1. Ensure a Bind for the portal name is executed (and succeeded) before Describe(P). Check logs for a prior Bind error.
  2. Keep the Parse→Bind→Describe→Execute sequence on one connection without reconnects; disable pooling or statement reuse that spans connections.
  3. If streaming with cursors, re-open the portal (re-Bind) once results are exhausted instead of describing a completed portal.
Defensive patterns

Strategy: retry

Try / catch

try {
  await describePortal(portalName);
} catch (e) {
  if (e.code === '34000' || /portal .* does not exist/.test(e.message)) {
    // re-run Bind to recreate the portal, then retry Describe
  } else throw e;
}

Prevention

When it happens

Trigger: Client sends Describe(P, name) before any Bind created that portal, after the portal completed (portal state is cleared/rowOffset reset on completion), or after a connection reset cleared the portals map.

Common situations: Driver-side connection failover between Bind and Describe; cursor-style streaming clients that expect portals to persist across transactions; a failed earlier Bind (e.g. binary-format rejection from errors 1280/1281) meant the portal was never created.

Related errors


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