The misleading error — Why 'invalid port number' isn't about the port
A developer on Stack Overflow reported this exact symptom after setting up Prisma with a local PostgreSQL instance:
Error: undefined: invalid port number in "postgresql://postgres:password@localhost:5432/linker"
The port 5432 is correct, PostgreSQL is listening, and pgAdmin confirms the server is reachable. Yet Prisma refuses to connect. The error message points at the port, but the real culprit is often a password that contains characters with special meaning in a URL — @, #, :, and others. In the Stack Overflow example, the password password is benign; the user's actual issue likely stemmed from a missing or misconfigured .env file. However, the same error message routinely appears when the password itself breaks URL parsing, and that scenario is what this article addresses. The fix is to percent-encode those characters so the connection string stays intact.
Root cause — Special characters in the password break URL parsing
Prisma reads the DATABASE_URL environment variable and parses it as a standard URL. The format is:
postgresql://user:password@host:port/database
If the password contains a special character like @, #, :, or %, the parser sees an extra delimiter and misinterprets the host, port, or path. For example, a password p@ss turns the URL into:
postgresql://postgres:p@ss@localhost:5432/linker
The parser splits on the first @, treating p as the password and ss@localhost:5432/linker as the host. Since ss@localhost:5432 is not a valid hostname, the error surfaces as an "invalid port number" — the parser can't extract a port from the mangled host segment. The same thing happens with # (fragment delimiter), : (port delimiter), % (escape prefix), and several other special characters.
The Prisma schema itself is not at fault; the datasource db block simply delegates to the environment variable:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
The fix is to percent-encode every special character in the password so the URL parser treats them as literal values.
The fix — Percent-encode special characters in DATABASE_URL
Replace each special character in your password with its percent-encoding (also called URL encoding). The accepted answer on the Stack Overflow question provides a complete reference table. Here are the most common ones:
| Character | Percent-encoding |
|---|---|
! |
%21 |
# |
%23 |
$ |
%24 |
% |
%25 |
& |
%26 |
' |
%27 |
( |
%28 |
) |
%29 |
* |
%2A |
+ |
%2B |
, |
%2C |
/ |
%2F |
: |
%3A |
; |
%3B |
= |
%3D |
? |
%3F |
@ |
%40 |
[ |
%5B |
] |
%5D |
space |
%20 |
" |
%22 |
- |
%2D |
. |
%2E |
< |
%3C |
> |
%3E |
\ |
%5C |
^ |
%5E |
_ |
%5F |
` |
%60 |
{ |
%7B |
| ` | ` |
} |
%7D |
~ |
%7E |
For example, if your password is p@ss#word, the encoded version is p%40ss%23word. Your .env file then becomes:
DATABASE_URL="postgresql://postgres:p%40ss%23word@localhost:5432/linker"
Manually replacing characters is error-prone. A safer approach is to use Node.js to encode the password programmatically. Open a terminal and run:
node -e "console.log(encodeURIComponent('p@ss#word'))"
The output is the percent-encoded password:
p%40ss%23word
Copy the result (without quotes) and paste it into your DATABASE_URL. This method works for any password, including those with Unicode characters like £ or 円.
After updating the .env file, restart your Prisma workflow. If you were running npx prisma studio or npx prisma migrate dev, stop it and start again so the new environment variable is picked up.
Verify — Test the connection after encoding
Run a simple Prisma command that touches the database, such as prisma db push:
npx prisma db push
If the connection succeeds, you'll see output like:
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": PostgreSQL database "linker", schema "public" at "localhost:5432"
Your database is now in sync with your Prisma schema. Running generate... (Use --skip-generate to skip the generators)
If you still get the same error, double-check that you encoded every special character. A common mistake is leaving the @ in the password unencoded because it looks like the host separator. Also, ensure you didn't accidentally encode the : between user and password — only the password itself needs encoding.
For a more direct test, you can use psql with the original (unencoded) password to confirm PostgreSQL accepts the credentials:
PGPASSWORD='p@ss#word' psql -h localhost -p 5432 -U postgres -d linker -c "SELECT 1;"
If that works, Prisma will work too.
Two patterns that still trip you up
1. The password contains a % character
If your password already contains a literal %, you must encode it as %25. For example, pass%word becomes pass%25word. Failing to do this leaves a bare % that the parser interprets as the start of an escape sequence, often producing a different error or a malformed URL.
2. You're using a pooled connection string from Prisma Postgres
When you use Prisma's hosted Postgres, the official connection string format includes a ?sslmode=require query parameter. If your password contains special characters, they must still be encoded, but now the URL has additional delimiters (?, &). The same encoding rules apply: encode the password portion only, not the entire URL. For example:
# Pooled connection with encoded password
DATABASE_URL="postgres://USER:p%40ss%23word@pooled.db.prisma.io:5432/?sslmode=require"
If you're on an M1/M2 Mac and encounter a different connectivity error (Can't reach database server at database:5432), that's a separate issue related to Docker networking — see the M1 Prisma connectivity guide for the fix.
FAQ
Why does Prisma say "invalid port number" when my port is correct?
The error is misleading. It usually means a special character in your password (like @, #, or %) is breaking the URL parser. Percent-encode those characters to fix it.
How do I percent-encode my PostgreSQL password for Prisma?
Replace each special character with its percent-encoding (e.g., @ becomes %40). You can use a Node.js one-liner: node -e "console.log(encodeURIComponent('your_password'))" to get the encoded version.
What if my password contains a % sign?
Encode it as %25. For example, pass%word becomes pass%25word. Otherwise the parser will treat the following characters as an escape sequence.
Can I just remove special characters from my password?
Yes, that's a valid workaround. If you control the PostgreSQL user, you can set a password that contains only alphanumeric characters and avoid encoding altogether. However, for managed databases where you can't change the password, encoding is the only option.
Does this affect Prisma Studio or only migrations?
It affects every Prisma command that reads DATABASE_URL, including prisma studio, prisma migrate, prisma db push, and the generated Prisma Client at runtime. The fix is the same everywhere.
Related
- Prisma: Can't reach database server at database:5432 on M1 — a different connectivity race on Apple Silicon.
- prepared statement s0 already exists: Fix for Prisma + PgBouncer — another common Prisma/Postgres error.
- How to Change a PostgreSQL User Password (Supabase) — if you decide to set a simpler password.
Originally published at https://www.iloveblogs.blog
Top comments (0)