Describe the bug
DatabaseSessionService's connection-string parser (getConnectionOptionsFromUri in core/src/sessions/db/operations.ts) is incompatible with both of the standard Postgres URL forms used to connect to a Cloud SQL instance over a Unix-domain socket. The result is that adk api_server --session_service_uri=... can't be pointed at a Cloud SQL–backed Postgres from inside Cloud Run or App Engine, even though the exact same connection string works with pg/knex when used directly.
The parser does:
const { host, port, username, password, pathname } = new URL(uri);
const hostName = host.split(":")[0];
// ...
return { host: hostName, port, user: username, password, dbName: pathname.slice(1), driver, entities };
It only reads the URL's authority, never looks at URL.search, and never decodes percent-encoding. That breaks two forms, in two different ways:
Form 1 — query-parameter host (pg's alternate URL form for sockets):
postgresql://user:pass@/dbname?host=/cloudsql/my-project:us-central1:my-instance
This fails even earlier. Node's WHATWG URL implementation throws on the empty authority between @ and /:
TypeError [ERR_INVALID_URL]
input: 'postgresql://user:pass@/dbname?host=/cloudsql/my-project:us-central1:my-instance'
so new URL(uri) throws before getConnectionOptionsFromUri can return. And even if the parse succeeded, the function never consults URL.search, so the host=… query parameter is silently dropped. This is likely the same root cause as #279 (?sslmode= and other query-string options ignored), generalized.
Form 2 — percent-encoded Unix-socket host (pg's primary URL form for sockets):
postgresql://user:pass@%2Fcloudsql%2Fmy-project%3Aus-central1%3Amy-instance/dbname
new URL() parses this, but because postgresql is a non-special scheme the WHATWG URL parser preserves percent-encoding inside URL.host. The parser then takes host.split(':')[0] verbatim — %2Fcloudsql%2Fmy-project%3Aus-central1%3Amy-instance — and passes it as host: to MikroORM's PostgreSqlDriver. The driver (via knex → pg) treats it as a TCP hostname and pg.Client.connect() calls net.createConnection({ host, port: 5432 }), which fails:
Failed to create session: Error: getaddrinfo EAI_AGAIN %2Fcloudsql%2Fmy-project%3Aus-central1%3Amy-instance
pg itself supports this URL form via its own connection-string parser, but the ADK pre-decomposes the URL and never hands pg the original string, so that support is bypassed.
To Reproduce
- Provision any Cloud SQL Postgres instance and attach it to a Cloud Run service via
gcloud run services update ... --add-cloudsql-instances=<instance-connection-name> so the Unix socket is mounted at /cloudsql/<instance-connection-name>/.s.PGSQL.5432.
- Install
@google/adk and @google/adk-devtools.
- Start the api_server inside the container with a Unix-socket session URI, e.g.:
npx adk api_server /app/agents/myAgent \
--port=8000 --host=0.0.0.0 \
--session_service_uri='postgresql://user:pass@%2Fcloudsql%2Fmy-project%3Aus-central1%3Amy-instance/mydb'
- Issue any session create:
curl -XPOST https://<service>/apps/myAgent/users/u1/sessions/s1.
- Observe:
ERROR: [ADK API Server] Failed to create session:
Error: getaddrinfo EAI_AGAIN %2Fcloudsql%2Fmy-project%3Aus-central1%3Amy-instance
- Re-run with Form 2 (
--session_service_uri='postgresql://user:pass@/mydb?host=/cloudsql/my-project:us-central1:my-instance'). The server fails at startup with TypeError [ERR_INVALID_URL].
Both forms work as expected when passed directly to pg:
import pg from 'pg';
const c = new pg.Client('postgresql://user:pass@%2Fcloudsql%2Fmy-project%3Aus-central1%3Amy-instance/mydb');
await c.connect(); // succeeds
Expected behavior
Standard Postgres Unix-socket connection strings should work with --session_service_uri, matching the behavior of pg/knex/libpq. Concretely, both forms below should connect successfully:
postgresql://user:pass@%2Fcloudsql%2F<instance>/<db>
postgresql://user:pass@/<db>?host=/cloudsql/<instance>
Additional context
- Deployment shape: Cloud Run service with
--add-cloudsql-instances=<INSTANCE_CONNECTION_NAME> (the Google-recommended way to talk to Cloud SQL from Cloud Run without a sidecar). Unix socket appears at /cloudsql/<INSTANCE_CONNECTION_NAME>/.s.PGSQL.5432; this is the standard path documented at https://cloud.google.com/sql/docs/postgres/connect-run#connect-built-in.
- Separately, noticed while digging:
core/src/cli/cli.ts defines the option with the flag string and description merged into a single argument:
new Option(
"--session_service_uri <string>, Optional. The URI of the session service. Supported URIs: memory:// for in-memory session service."
);
vs. the adjacent --log_level which uses the documented two-argument form. Commander 14 is permissive enough that this still parses correctly today, but it's a latent bug in the same file — any future Commander tightening on flag-string validation would break --session_service_uri parsing entirely. Probably worth fixing in the same PR since anyone touching this area will be reading cli.ts anyway. Same applies to --artifact_service_uri right below.
Describe the bug
DatabaseSessionService's connection-string parser (getConnectionOptionsFromUriincore/src/sessions/db/operations.ts) is incompatible with both of the standard Postgres URL forms used to connect to a Cloud SQL instance over a Unix-domain socket. The result is thatadk api_server --session_service_uri=...can't be pointed at a Cloud SQL–backed Postgres from inside Cloud Run or App Engine, even though the exact same connection string works withpg/knexwhen used directly.The parser does:
It only reads the URL's authority, never looks at
URL.search, and never decodes percent-encoding. That breaks two forms, in two different ways:Form 1 — query-parameter host (pg's alternate URL form for sockets):
This fails even earlier. Node's WHATWG URL implementation throws on the empty authority between
@and/:so
new URL(uri)throws beforegetConnectionOptionsFromUrican return. And even if the parse succeeded, the function never consultsURL.search, so thehost=…query parameter is silently dropped. This is likely the same root cause as #279 (?sslmode=and other query-string options ignored), generalized.Form 2 — percent-encoded Unix-socket host (pg's primary URL form for sockets):
new URL()parses this, but becausepostgresqlis a non-special scheme the WHATWG URL parser preserves percent-encoding insideURL.host. The parser then takeshost.split(':')[0]verbatim —%2Fcloudsql%2Fmy-project%3Aus-central1%3Amy-instance— and passes it ashost:to MikroORM's PostgreSqlDriver. The driver (via knex → pg) treats it as a TCP hostname andpg.Client.connect()callsnet.createConnection({ host, port: 5432 }), which fails:pgitself supports this URL form via its own connection-string parser, but the ADK pre-decomposes the URL and never hands pg the original string, so that support is bypassed.To Reproduce
gcloud run services update ... --add-cloudsql-instances=<instance-connection-name>so the Unix socket is mounted at/cloudsql/<instance-connection-name>/.s.PGSQL.5432.@google/adkand@google/adk-devtools.npx adk api_server /app/agents/myAgent \ --port=8000 --host=0.0.0.0 \ --session_service_uri='postgresql://user:pass@%2Fcloudsql%2Fmy-project%3Aus-central1%3Amy-instance/mydb'curl -XPOST https://<service>/apps/myAgent/users/u1/sessions/s1.--session_service_uri='postgresql://user:pass@/mydb?host=/cloudsql/my-project:us-central1:my-instance'). The server fails at startup withTypeError [ERR_INVALID_URL].Both forms work as expected when passed directly to
pg:Expected behavior
Standard Postgres Unix-socket connection strings should work with
--session_service_uri, matching the behavior ofpg/knex/libpq. Concretely, both forms below should connect successfully:postgresql://user:pass@%2Fcloudsql%2F<instance>/<db>postgresql://user:pass@/<db>?host=/cloudsql/<instance>Additional context
--add-cloudsql-instances=<INSTANCE_CONNECTION_NAME>(the Google-recommended way to talk to Cloud SQL from Cloud Run without a sidecar). Unix socket appears at/cloudsql/<INSTANCE_CONNECTION_NAME>/.s.PGSQL.5432; this is the standard path documented at https://cloud.google.com/sql/docs/postgres/connect-run#connect-built-in.core/src/cli/cli.tsdefines the option with the flag string and description merged into a single argument:--log_levelwhich uses the documented two-argument form. Commander 14 is permissive enough that this still parses correctly today, but it's a latent bug in the same file — any future Commander tightening on flag-string validation would break--session_service_uriparsing entirely. Probably worth fixing in the same PR since anyone touching this area will be readingcli.tsanyway. Same applies to--artifact_service_uriright below.