Skip to content
Payment Emulator LabDocumentationOpen console

Operations and validation

Troubleshooting

These cases come from current startup guards, scripts or failures already foundwhile building the repository.

6 min read

Índice / Index · Español

These cases come from current startup guards, scripts or failures already found while building the repository.

#The API exits during startup with ADMIN_TOKEN is required

#Cause

RolesGuard.onModuleInit() refuses to start without a token of at least 16 characters. The token bootstraps the first administrator and drives CI/seed.

#Fix

Set it in the API process environment. For PowerShell:

powershell
$env:ADMIN_TOKEN = 'replace-with-at-least-16-characters'
npm run dev:api

The console's server-side NUXT_ADMIN_TOKEN and the seed's ADMIN_TOKEN must match when they use the root credential.

#The console starts but nobody can sign in

#Cause

A fresh database has no users. ADMIN_TOKEN is not a console password and does not create a person automatically.

#Fix

Run npm run admin:create against the running API; it asks for an address and a password and verifies the account by signing in with it. npm run seed also creates one, along with the demo data. Both are described in Getting started. Also ensure NUXT_SESSION_PASSWORD (or Compose's SESSION_PASSWORD) is at least 32 characters.

#I forgot the administrator password

If the account already exists, run npm run password:reset -- --email user@example.test from the repository root, with your email and the API running. It prompts for a new password with hidden input and revokes prior sessions. See Reset password for requirements and options.

#Integration tests are skipped

#Symptom

Vitest reports specs under test/integration/ as skipped while unit tests pass.

#Cause

The harness deliberately checks for DATABASE_URL. It will not guess a database and risk writing to the wrong one.

#Fix

Start a disposable PostgreSQL instance and set DATABASE_URL before npm test. See Testing. Do not point the suite at a database containing useful data.

#Docker commands cannot connect to the engine

#Symptom

docker version shows a client but cannot connect to the Docker Desktop Linux engine or named pipe.

#Cause

Docker Desktop/WSL services are not running. This is also why historical local validation in HANDOFF.md could not exercise Compose.

#Fix

Start Docker Desktop and wait until docker version reports both Client and Server. Then run:

bash
docker compose config
docker compose up --build --wait

The current CI exercises the full stack on Linux even when a Windows workstation cannot start its local engine.

#Typecheck or build prints Vue/Nuxt dependency warnings

#Symptom

npm run typecheck can print vue-router/volar/sfc-route-blocks ERR_PACKAGE_PATH_NOT_EXPORTED, and a build on newer Node.js versions can print DEP0155 warnings from Vue or PrimeUI packages.

#Meaning

These warnings come from the current Nuxt/Vue dependency graph. During the last repository validation, typecheck and all three workspace builds still exited successfully. Treat the process exit code as the immediate result, but do not silence the warnings: dependency alignment should remove them in a future maintenance pass.

#A changed JSON scenario has no effect

#Cause

Runtime built-ins come from generated apps/api/src/modules/scenarios/default-scenarios.ts, not directly from JSON.

#Fix

bash
npm run scenario:compile

Typecheck, tests and build run this automatically, but the API dev process does not have a scenario pre-hook. Restart it after regeneration.

Do not edit the generated TypeScript file; the next compile overwrites it.

#Webhooks remain scheduled

#Checks

  1. Confirm the worker is running; the API intentionally never opens a queue. It prints [queue] driver: … on the line it starts with.
  2. Confirm whatever that driver needs is reachable — REDIS_URL for bullmq, MONGO_URL for mongo. The default, postgres, needs nothing further. run-local.bat doctor reports the driver and its dependency together, which is worth doing before anything else: the script used to check one Redis port and start a worker that dialled another.
  3. Set RUNTIME_OUTBOUND_ENABLED=true in the worker process.
  4. Check that the callback host resolves to a public address. If it is internal on purpose -- a machine on your network, localhost -- list it in WEBHOOK_ALLOWED_HOSTS, which is the exception list to that rule.
  5. From a container, use host.docker.internal for a callback running on the host; localhost inside the container means the container itself. Outside one, the reverse: host.docker.internal resolves to a Docker adapter that nothing is listening on when Docker is not running.

The outbox row is durable. A temporary transport failure does not erase the owed notification; the dispatcher retries stale rows.

#A webhook is delivered more than once

This is valid. Delivery is at-least-once, and scenarios can request duplicates deliberately. The receiving integration must deduplicate using its normal provider strategy. Inspect the event and attempts in the console before treating the duplicate as a worker defect.

#A list filter is ignored or rejected

The supported wire shape is:

text
?page=2&pageSize=25&sort=-createdAt,name&search=ana&filter[state]=captured

Each endpoint has a field allow-list. An undeclared sort/filter is a 400, not a silently ignored parameter. If all filters appear ignored in a custom Nest test harness, ensure it calls configureApp(); that function enables Express's extended query parser and is shared by production and the official harness.

#Mercado Pago SDK requests cannot be redirected to the emulator

This is a documented fidelity limit, not a missing environment variable. The installed Mercado Pago SDK keeps its production API host in a non-configurable static field. Its webhook validator is used, but request compatibility is tested through the emulator's own conformance suite. See Mercado Pago fidelity.

#Stripe returns provider_operation_not_found for update

POST /v1/payment_intents/{id} is deliberately not routed. Mapping update onto confirm would attempt a payment that the caller only intended to edit. This gap is listed in Stripe fidelity.

#Migrations appear not to run from the repository root

Use the root command, which reads .env and .env.local, builds the API if it has to and waits for PostgreSQL:

bash
npm run migrate

The migration directory is anchored to the MikroORM config file so source and compiled execution use the matching migrations/ folder. In Compose, the API container runs node dist/cli/migrate.js before starting HTTP, so migrating by hand is rarely needed there.

#The migration command or the development worker fails to start

Migrations and the worker run from dist/, never through tsx: the entities carry their property types in emitDecoratorMetadata, which esbuild does not emit, so under tsx no entity is discovered at all. That is why start:worker:dev cannot work.

bash
npm run migrate
npm run start:worker --workspace @payment-emulator/api

Supply the database and operator environment first. The migration command runs pending migrations on the selected database; use a disposable database for tests. npm run migrate -- --fresh empties one by reverting every migration and applying them again, which needs no superuser and no psql.

Payment Emulator Lab · RonuSoftwareMIT