Strapi refuses to start and throws this from deep inside node_modules:
node_modules/pg/lib/sasl.js:24
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string')The wording sends people hunting for SASL configuration, authentication mechanisms, and PostgreSQL pg_hba.conf settings. None of that is the problem.
What the error actually means
PostgreSQL 10+ uses SCRAM-SHA-256 for password authentication. When the pg Node driver starts that handshake, it needs to hash the password, and hashing requires a string. The driver checks the type first and throws this error if it gets anything else.
In practice, "anything else" is almost always undefined.
So the real message is: Strapi passed undefined as the database password. The password is missing, not wrong. A wrong password produces a different error (password authentication failed for user "...").
This is a type check failing before any network traffic reaches your database.
Confirm it in ten seconds
Add a temporary log line at the top of config/database:
export default ({ env }) => {
console.log('DB password type:', typeof env('DATABASE_PASSWORD'));
console.log('DB password set:', Boolean(env('DATABASE_PASSWORD')));
return {
connection: {
client: 'postgres',
connection: {
host: env('DATABASE_HOST', '127.0.0.1'),
port: env.int('DATABASE_PORT', 5432),
database: env('DATABASE_NAME', 'strapi'),
user: env('DATABASE_USERNAME', 'strapi'),
password: env('DATABASE_PASSWORD'),
ssl: env.bool('DATABASE_SSL', false),
},
},
};
};If that prints undefined / false, you have confirmed the diagnosis. Remove the log lines before committing, and never log the value itself, only its type.
The causes, roughly in order of frequency
1. DATABASE_PASSWORD is not set in the environment
The most common case. Your config/database reads env('DATABASE_PASSWORD') with no fallback, and the variable does not exist.
Check that .env exists at the project root, sits next to package.json, and contains the key:
DATABASE_CLIENT=postgres
DATABASE_HOST=127.0.0.1
DATABASE_PORT=5432
DATABASE_NAME=strapi
DATABASE_USERNAME=strapi
DATABASE_PASSWORD=your-actual-passwordCommon variations on this mistake:
- The file is named
.env.local,env, or.env.txt. Strapi loads.env. - The file is in a subdirectory. Strapi loads it from the project root only.
- You are running the process from a different working directory, so the relative path to
.envdoes not resolve.
2. The Postgres role genuinely has no password
Fresh local installs of PostgreSQL often create a postgres superuser with no password set, relying on peer authentication for local socket connections. Strapi connects over TCP, which requires a password.
sudo -u postgres psql
\password postgresThen set the same value in your .env.
3. A deployed environment where the variable was never added
Everything works locally and the deploy fails. The .env file is (correctly) gitignored, and nobody added the variables to the platform.
- Docker Compose:
env_file: .envor an explicitenvironment:block. A.envnext todocker-compose.ymlpopulates interpolation in the compose file, which is not the same as being present inside the container. - Dockerfile:
.dockerignorealmost certainly excludes.env, which is correct. Inject the values at runtime. - Kubernetes: the Secret exists but is not referenced by
envFromorenvin the pod spec. - PaaS (Render, Railway, Fly, Heroku): the variable was set in one environment and not the other.
4. The connection string overrides your individual fields
If DATABASE_URL is set, and your config uses connectionString, it overrides every other connection.connection property. A DATABASE_URL that is malformed or has no password section produces exactly this error while your correctly-set DATABASE_PASSWORD sits there being ignored.
postgresql://user:password@host:5432/dbnameCheck for a missing :password, or special characters that need percent-encoding: @ → %40, # → %23, / → %2F, : → %3A.
5. env.int() or another typed helper on the password
password: env.int('DATABASE_PASSWORD'), // wrong
password: env('DATABASE_PASSWORD'), // rightA numeric password read through env.int() becomes a Number, and the driver rejects it for exactly the reason the error states. Rare, but it produces the most confusing version of this bug, because the variable is set.
6. An environment-specific config file that shadows the base one
config/env/production/database.ts completely replaces config/database.ts when NODE_ENV=production. If the production variant was written with different variable names, such as DB_PASSWORD instead of DATABASE_PASSWORD, you get an app that works in development and fails on deploy.
Check whether the directory exists:
ls -la config/env/Making the failure legible next time
Two habits keep this from costing an afternoon.
Fail fast with a clear message. Validate required variables at the top of the config:
export default ({ env }) => {
const password = env('DATABASE_PASSWORD');
if (typeof password !== 'string' || password.length === 0) {
throw new Error(
'DATABASE_PASSWORD is missing or empty. Set it in .env (local) or in your platform environment (deployed).'
);
}
return {
connection: {
client: 'postgres',
connection: {
host: env('DATABASE_HOST', '127.0.0.1'),
port: env.int('DATABASE_PORT', 5432),
database: env('DATABASE_NAME', 'strapi'),
user: env('DATABASE_USERNAME', 'strapi'),
password,
ssl: env.bool('DATABASE_SSL', false),
},
},
};
};Keep a committed .env.example listing every key with placeholder values, so a new environment has a checklist rather than an archaeology exercise.
Related errors and what they mean instead
| Error | Meaning |
|---|---|
client password must be a string | Password is undefined or not a string |
password authentication failed for user "x" | Password is present but wrong |
role "x" does not exist | Username is wrong or the role was never created |
database "x" does not exist | Database name is wrong or was never created |
ECONNREFUSED | Nothing is listening on that host and port |
Knex: Timeout acquiring a connection | Usually a firewall dropping packets; see the pool timeout guide |
Cannot destructure property 'client' of 'db.config.connection' | The database config file itself did not load |




