Skip to content

A postgres config.url that pg itself cannot parse passes the authoring door — the shared credential-free URL check deliberately never parses #9091

Description

@os-project-manager

Found while implementing #8873 (the postgres DSN credential-injection arm) and filed rather than fixed there: it lands in packages/spec, and it is a different mechanism one door up.

The gap

PostgresConfigSchema.url documents a grammar in its own describe text —

Format: postgresql://[user@][host][:port][/dbname][?params]

— and enforces none of it. The value goes through placeholderFree(credentialFreeUrl(z.string(), …)), and both of those are string-boundary scans: they find userinfo, credential query parameters and ${…} spans. Neither asks whether the string is a DSN the client can open.

That is deliberate at the helper, and correctly so. common.zod.ts says it twice, in both halves:

Deliberately NOT new URL(): real DSNs take forms WHATWG parsing rejects or mangles

new URL() rejects the multi-host DSN form outright (#8696)

The multi-host form is the motivating case, and for mongo it is a real, working, documented shape (mongodb://app@h1:27017,h2:27017/app), so refusing it at the door would be wrong. The helper is shared across drivers, so its correct leniency for mongo becomes no check at all for postgres — where the same form is not a working shape but an unopenable one.

Measured

pg 8.22.0 does not implement libpq's multi-host DSN. Both its parser and its client constructor reject it:

parse('postgresql://app@h1:5432,h2:5433/app') -> TypeError [ERR_INVALID_URL]
new ConnectionParameters({connectionString: <the same string>}) -> TypeError [ERR_INVALID_URL]

PostgresConfigSchema accepts that exact value today. So an operator can publish a postgres datasource, see it saved, and discover only at connect time that the url can never open a connection — with a bare Invalid URL whose own input field pg redacts, i.e. an error that does not name the value that caused it.

Why it is worth a card rather than a shrug

This is the declared-≠-enforced shape of Prime Directive #10 at the authoring door: the schema publishes a format and validates a different, narrower property of the string. The connection form is the surface an operator (or an AI author) reaches for, and the one thing it could cheaply tell them — this is not a url this driver can open — is the thing it does not.

Note the fix is per-driver, not in the shared helper: credentialFreeUrl must keep refusing to parse, because mongo's multi-host and +srv forms depend on that. The candidate shape is a postgres-specific superRefine on url that asks the postgres client's own grammar, the same way #8873 made the runtime arm ask it rather than model it.

Scope note

Not a duplicate of #9041, which is about a different contradiction (credentialsRef bound while the url names no user — a semantic pair, both halves individually valid). This one is a url that is not a url for this driver at all.

#8873 is not addressed here and is unrelated in mechanism; it only made the runtime failure earlier and named (see #9090). Filed unassigned for triage.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions