Background
I’m setting up Knex migrations on a PostgreSQL database running inside WSL2 (Ubuntu). My Node.js/Knex app (also in WSL) connects over the WSL IP to this Postgres instance. Everything appears configured correctly, but npx knex migrate:latest errors out claiming the migrations table already exists—even though I’ve dropped it and can confirm via psql that it’s gone just before running Knex.
Environment
- OS: Ubuntu (WSL2 on Windows)
- Node.js: node -v22.12.0
- PostgreSQL: 16.8 (inside WSL)
- knex: 3.1.0
- pg (Node driver): 8.15.6
What happens
Connect via psql to confirm migrations table is gone:
SELECT EXISTS ( SELECT 1 FROM pg_class WHERE relname='knex_migrations');-- Returns: f```Immediately run migrations:
npx knex migrate:latest --env development```Knex fails during its internal
CREATE TABLE "public"."knex_migrations"step:error create table "public"."knex_migrations" ... - relation "knex_migrations" already exists
Knexfile.js(development)
require('dotenv').config();const pg = require('pg');const client = new pg.Client({ host: process.env.DEV_DB_HOST || '172.21.10.102', port: process.env.DEV_DB_PORT || 5432, database: process.env.DEV_DB_NAME || 'angushallyapp_dev', user: process.env.DEV_DB_USER || 'postgres', password: process.env.DEV_DB_PASSWORD || null});client.connect() .then(() => { console.log('KNEXFILE_DEBUG: Connected to DB via direct pg client.'); return client.query('SELECT current_schema()'); }) .then(res => { console.log('KNEXFILE_DEBUG: current_schema() is:', res.rows[0].current_schema); return client.query("SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'knex_migrations' AND table_schema = 'public');"); }) .then(res => { console.log('KNEXFILE_DEBUG: public.knex_migrations exists (direct pg check):', res.rows[0].exists); return client.end(); }) .then(() => console.log('KNEXFILE_DEBUG: Direct pg client disconnected.')) .catch(err => { console.error('KNEXFILE_DEBUG: Error in direct pg client connection or query:', err); client.end(); });module.exports = { development: { client: 'postgresql', connection: { host : process.env.DEV_DB_HOST || '172.21.10.102', port : process.env.DEV_DB_PORT || 5432, database : process.env.DEV_DB_NAME || 'angushallyapp_dev', user : process.env.DEV_DB_USER || 'postgres', password : process.env.DEV_DB_PASSWORD || null, searchPath: ['public'] }, pool: { min: 2, max: 10 }, migrations: { directory: './migrations', tableName: 'public.knex_migrations', loadExtensions: ['.js'] }, seeds: { directory: './seeds' }, debug: true, log: { warn(message) { console.warn('Knex Warning:', message); }, error(message) { console.error('Knex Error:', message); }, deprecate(message) { console.warn('Knex Deprecation:', message); }, debug(message) { console.log('Knex Debug:', message); } } }, production: { client: 'postgresql', connection: { connectionString: process.env.DATABASE_URL, ssl: { rejectUnauthorized: false }, search_path: ['identity', 'habit', 'crm', 'fsa', 'public'] }, pool: { min: 2, max: 10 }, migrations: { directory: './migrations', tableName: 'public.knex_migrations', loadExtensions: ['.js'] }, seeds: { directory: './seeds' } }};Key Debug Output
Knex’s debug logs show that right before attempting its DDL, a direct PG client (using the same creds) confirms the table does not exist. Immediately afterward, the internal CREATE TABLE "public"."knex_migrations" fails with “already exists.”
Things I've tried
- Confirmed pg_hba.conf allows md5 for angus_dev on all addresses.
- Set listen_addresses = '*' in postgresql.conf.
- Specified tableName: 'public.knex_migrations' in the Knex config.
- Simplified searchPath to ['public'].
- knex migrate:rollback --all (also errors with the same “already exists”).
- Restarted Postgres service in WSL.
- Granted SUPERUSER and DB ownership to angus_dev.
What I don't getWhy is Knex’s internal DDL thinking that public.knex_migrations already exists when a direct check (and psql) confirms it does not? Could this be related to transaction handling, a driver quirk, or something specific about WSL networking/Postgres? Any ideas on what else might be causing this phantom “relation already exists” error?
Thank you to whoever can help.