Skip to content

Troubleshooting

Karthik P edited this page Oct 15, 2026 · 1 revision

Troubleshooting

This page collects the problems you are most likely to meet, grouped by where they show up: the installer, container and process startup, sign-in, runs, individual jobs, development and tests. Each row gives the symptom or exact message, the cause, and the fix. Messages are quoted from the code, so you can search for them in the logs. Start with the two health endpoints and the logs described under First checks.

First checks

Check Command Healthy result
Containers docker compose ps (in the install folder) postgres, redis and api healthy; worker and web running; migrate exited 0
Database and Redis curl http://localhost:8080/api/health (dev: port 4000) {"database":"up","redis":"up"}; HTTP 503 with down otherwise
Worker curl http://localhost:8080/api/health/worker {"worker":"up"}; HTTP 503 {"worker":"down"} when no worker is connected to the queue
Logs docker compose logs -f, or docker compose logs worker / api / migrate

The run page also shows each run's stop reason and each job's error.

Installer

Symptom Cause Fix
Docker is not installed. docker is not on the path Install Docker Desktop and run the installer again.
Docker Compose v2 is missing. docker compose version fails (only the old docker-compose is present) Update Docker.
Docker is installed but not running. docker info fails Start Docker Desktop (or the Docker service), wait until it reports running, and run the installer again.
Could not find the latest release on GitHub. The GitHub API was unreachable or rate-limited Set JOB_MATCHER_VERSION, for example JOB_MATCHER_VERSION=1.0.0, and run it again.
A database from an earlier Job Matcher install exists, but its .env is gone. The install folder was deleted without docker compose down -v; the old volume was created with a password that is now lost Restore the old .env into the install folder, or delete the old data: docker volume rm job-matcher_postgres-data job-matcher_redis-data.
Live mode needs both keys; starting in demo mode. Only one of the two keys was entered Add both keys to .env, set PROVIDERS=live, and run docker compose up -d.
The stack did not become healthy. A service failed or exceeded the 300 s wait Run docker compose logs in the install folder and look up the error below. A first install also downloads the images, which can be slow.
denied when pulling images An expired GHCR login is being sent; the images are public docker logout ghcr.io, then run the installer again.
The installer picked port 8081 or higher 8080 was in use Expected. The URL it prints, and BETTER_AUTH_URL and WEB_PORT in .env, use the port it chose.
The prompts did not appear JOB_MATCHER_NONINTERACTIVE=1 was set, or no terminal was available Keys are then read from OPENROUTER_API_KEY and FIRECRAWL_API_KEY in the environment; add them to .env afterwards if needed.

Container and process startup

Symptom Cause Fix
Set POSTGRES_PASSWORD in .env POSTGRES_PASSWORD is empty in a self-hosted .env Set it to a long random value (openssl rand -hex 32). Only on a fresh install: an existing volume keeps the password it was created with.
api or worker exits at once with a schema decode error naming DATABASE_URL, API_PORT, BETTER_AUTH_SECRET, BETTER_AUTH_URL or REDIS_URL A core variable is missing or empty; config.ts validates them in both processes Set the variable in .env. API_PORT must be a number.
Decode error naming PROVIDERS A value other than live or fake Use live or fake.
worker exits with a decode error naming OPENROUTER_API_KEY PROVIDERS=live without an OpenRouter key Add the key, or set PROVIDERS=fake.
FIRECRAWL_MODE=cloud needs FIRECRAWL_API_KEY. Cloud or hybrid mode without a key Add FIRECRAWL_API_KEY.
FIRECRAWL_MODE=local needs FIRECRAWL_LOCAL_URL (e.g. http://localhost:3002). Local or hybrid mode without a URL Set FIRECRAWL_LOCAL_URL.
FIRECRAWL_MODE must be cloud, local or hybrid, and FIRECRAWL_CLOUD_PROXY basic or auto. A typo in either variable Fix the value.
FIRECRAWL_LOCAL_URL must be an http(s) URL, got "..." The URL has no scheme or the wrong one Use http://... or https://....
FIRECRAWL_CREDIT_RESERVE must be a whole number of credits, got "..." A decimal, negative or text value Use a whole number such as 50.
FIRECRAWL_LOCAL_SEARCH must be true or false, got "..." Another value Use true or false.
Decode error naming an OPENROUTER_* routing variable A value outside the allowed list (for example OPENROUTER_SERVICE_TIER=fast) See Configuration Reference.
migrate exits non-zero; api and worker never start A migration failed, or Postgres rejected the password docker compose logs migrate. A password error usually means .env does not match the volume (see the installer row above).
web never starts It waits for api to be healthy Fix the api first; docker compose logs api.
api unhealthy after changing API_PORT The healthcheck and the web image's proxy both use port 4000 Set API_PORT=4000 in the self-hosted stack.
/api/health returns 503 with "redis":"down" Redis is unreachable; health checks time out after 2 s Check the redis container, or REDIS_URL in development.
/api/health/worker returns 503 No worker is connected to the runs queue docker compose logs worker; it usually exited at startup with one of the errors above. Runs stay "Waiting to start" until a worker connects.

Sign-in and access

Symptom Cause Fix
Sign-in or sign-up fails with an "invalid origin" error The address in the browser does not match BETTER_AUTH_URL exactly; better-auth trusts only that origin Make them match, including scheme and port. localhost and 127.0.0.1 are different origins. Run docker compose up -d after editing .env.
Works on the host, fails from another device BETTER_AUTH_URL is still http://localhost:<port> Set it to http://<host-ip>:<port> (or your HTTPS domain) and restart.
Fails behind an HTTPS reverse proxy BETTER_AUTH_URL still uses http:// or the internal port Set it to the public origin, for example https://jobs.example.com.
Everyone is signed out after an upgrade or restore BETTER_AUTH_SECRET changed Keep .env with your backups; sign in again.
Port already in use Another program holds WEB_PORT Change WEB_PORT and the port in BETTER_AUTH_URL, then docker compose up -d.
Redirected to /home from a ?next= link The next value was unsafe (another site, an auth page, encoded slashes, control characters) Expected; only same-site paths are followed (safe-next.ts).

Starting a run

Symptom Cause Fix
Add a resume URL in settings before starting a run No resumeUrl saved Settings, Resume.
Source not found No sources selected, or a source belongs to another user or was deleted Select at least one of your sources on Analyze.
A run is already in progress (HTTP 409) Only one queued or running run per user is allowed Wait for it to finish. A run whose queue job was lost is ended automatically on the next start.
Could not queue the run. Try again shortly. (HTTP 503) Redis did not accept the job; the run is marked FAILED so it does not block later starts Check Redis, then start again.
Analyze never finishes; status stays "Waiting to start" No worker is running Check /api/health/worker and docker compose logs worker.
Scheduled runs do not happen scheduleMinutes is empty, no resume or sources, or a run was already active at the tick Set "Check every N minutes" (15 or more). Ticks are skipped while a run is active or before a resume and a source exist. The worker restores every schedule from the database when it starts.

Run stop reasons

A run that ends early is marked STOPPED or FAILED with one of these reasons. Errors that stop a run are defined by stopsRun() in errors.ts.

Stop reason Cause Fix
The OpenRouter API key is a management or provisioning key, which cannot run AI matching. Preflight found a management or provisioning key Create a regular API key at https://openrouter.ai/settings/keys.
The model '<model>' has no provider supporting JSON Schema structured output. The model cannot return strict JSON Choose another OPENROUTER_MODEL.
The model '<model>' has no endpoint from the allowed providers (...) supporting JSON Schema structured output. OPENROUTER_PROVIDERS excludes every compatible endpoint Change OPENROUTER_PROVIDERS or the model.
The model '<model>' has no flex endpoint from the allowed providers (...). OPENROUTER_SERVICE_TIER=flex but no flex endpoint is offered Change OPENROUTER_PROVIDERS, OPENROUTER_SERVICE_TIER or the model.
The model '<model>' has no ... endpoint ... under the price cap (...) Every endpoint costs more than the caps Raise OPENROUTER_MAX_PRICE_PROMPT and OPENROUTER_MAX_PRICE_COMPLETION, or choose another model.
An OpenRouter error with status 401 The key is invalid or revoked Replace OPENROUTER_API_KEY and restart the worker.
An OpenRouter error with status 402, 403, 404 or 429 Out of credit, forbidden, model not found, or rate-limited Check the OpenRouter account and model. (Flex capacity 429s during evaluation are retried first; see below.)
A Firecrawl error with status 401, 402 or 429 Invalid key, plan exhausted, or rate-limited Check the Firecrawl key and plan.
Local Firecrawl is not running at <url> local or hybrid mode and the self-hosted instance did not answer the health check before scraping Start it (in development, COMPOSE_PROFILES=firecrawl and bun run dev; it takes about 95 s to become ready) and check FIRECRAWL_LOCAL_URL.
Firecrawl credit reserve reached (N left until YYYY-MM-DD) A cloud run's sources would not fit above FIRECRAWL_CREDIT_RESERVE, or it has spent down to it Wait for the billing period to reset, lower the reserve, use hybrid or local mode, or prefer ATS sources (free).
Firecrawl daily credit allowance reached (N left until YYYY-MM-DD) A scheduled run used today's share of the credits above the reserve Expected; tomorrow's runs get a new share. Manual runs are not limited by the daily share. See Firecrawl Credits and Caching.
The run was interrupted when its worker stopped. Results saved so far are kept; start a new run to check the rest. The worker stopped mid-run, or BullMQ failed a stalled job Start a new run; jobs already evaluated in the same context are skipped.
The run was never picked up. Start a new run. The run was saved but its queue job was lost (for example the API stopped before queueing, or Redis lost it) Start a new run.
The run failed unexpectedly An unhandled error docker compose logs worker and open an issue with the log.

A failed title triage request does not stop the run (except for a refused API key): the worker logs Title triage failed: ... and checks jobs without triage.

Individual job errors

These fail one job and leave the rest of the run going.

Job error Cause Fix
The page is gone (HTTP 404). The posting was removed None needed.
The page returned HTTP <status>. Another HTTP error Usually temporary; it is retried on the next run in the same context.
The site blocked the cloud scraper (HTTP <status>). / ... the local scraper ... The site refused the scraper In cloud mode try FIRECRAWL_CLOUD_PROXY=auto; in local mode try hybrid.
The site served the local scraper a bot check or a page without its content. A challenge page or empty shell hybrid mode retries such pages on the cloud; after two local blocks in a row a site goes straight to the cloud for a week.
needs cloud; credit reserve reached hybrid mode: the page needs the cloud, but the run has no credits left above the reserve See the credit reserve row above.
OpenRouter flex capacity unavailable after 5 retries: ... Flex capacity was unavailable through back-offs of 1, 2, 4, 8 and 16 minutes Try later, or set OPENROUTER_SERVICE_TIER=default; the price caps may then need raising, since non-flex endpoints cost more.
OpenRouter completion did not finish: <reason> / OpenRouter returned no completion choices. The model stopped early or returned nothing Retried on the next run; persistent failures suggest another model.
Could not save the match: ... The database rejected the match row Use the retry button on the run page (POST /api/runs/:id/results/:resultId/save).
Set the <ATS> board name for <url>. An ATS source has no board key Settings, Job sources: enter the board name.
No fake HTTP for <url>. Demo mode with an ATS board or another URL fetched over plain HTTP; the fake providers do not fake HTTP In demo mode, use a plain careers page URL or a search source.
Skipped by title triage Triage scored the title below 2 of 3 Expected. Turn triage off in Settings, Runs to check every job.
"The posting has closed (its page says it was valid until an earlier date)" The job page's structured data marks it expired Expected.
Search source skipped, with Search sources need cloud Firecrawl or SearXNG on your self-hosted Firecrawl (set FIRECRAWL_LOCAL_SEARCH=true) in the log local mode without SearXNG Run SearXNG for the Firecrawl container and set FIRECRAWL_LOCAL_SEARCH=true, or use cloud or hybrid mode.

Discord alerts

Symptom Cause Fix
Enter a Discord webhook URL when saving The URL is not https on discord.com, discordapp.com, ptb.discord.com or canary.discord.com with a /api/webhooks/ path Copy the webhook URL from the Discord channel's integrations settings.
Alerts are not being sent: this is not a Discord webhook URL. Replace it or clear it. A webhook saved before only Discord URLs were accepted Replace or clear it.
No alerts, no errors in the UI Alert failures are only logged, by design docker compose logs worker (and api, for alerts sent after a retried save). In demo mode alerts are only logged ("Fake Discord alert for ...").
A link in the alert points to localhost Run links use BETTER_AUTH_URL Set BETTER_AUTH_URL to the address you open the app on.

Development

Symptom Cause Fix
bun run db:migrate cannot reach the database Postgres is not running yet (bun run dev is what starts it) docker compose up -d --wait, then migrate.
prisma generate fails during bun install because DATABASE_URL is not set prisma.config.ts reads env("DATABASE_URL") Create .env from .env.example before installing, or set DATABASE_URL to any Postgres URL; generation never connects.
Port 3000, 4000, 5432 or 6379 already in use Another dev server or a local Postgres or Redis Stop it. Postgres's host port can move with POSTGRES_PORT (update DATABASE_URL to match); the others are fixed.
bun run dev waits a long time COMPOSE_PROFILES=firecrawl is set and Firecrawl is starting (about 95 s, about 6 GB of images on first pull) Wait, or remove the profile.
The worker refuses to start in development PROVIDERS=live without keys Set PROVIDERS=fake to work without keys.
Changes to .env have no effect The processes read .env at startup Restart bun run dev.
commit-msg: remove the Co-Authored-By / "Generated with" lines from the message. The commit-msg hook Remove those lines.
A commit is refused with lint or type errors The pre-commit hook runs bun run lint && bun run typecheck bun run format fixes formatting; fix the rest.
CI fails at bun install --frozen-lockfile bun.lock does not match package.json Run bun install locally and commit bun.lock.

Tests

Symptom Cause Fix
Tests must not call <url> A test reached a real provider or ATS host; test/setup.ts blocks them Use the fake services or fakeHttp() fixtures.
Tests pass but bun test fails on coverage A loaded source file is below 90% of lines or 75% of functions Add tests for that file. Running a single test file can trip this for files it only partly exercises; run the whole suite.
API tests fail to connect Postgres or Redis from .env is not running, or migrations are not applied docker compose up -d --wait and bun run db:migrate.
Playwright cannot start the API: port 4000 in use Playwright never reuses a running API, because a dev worker would be on live providers Stop bun run dev (or at least the API and worker) before bun run test:e2e.
e2e data shows up in development It should not: e2e uses <database>_e2e Check that DATABASE_URL is set as expected; Playwright appends _e2e to its database name.
A run in an e2e test never starts The worker is not connected Playwright waits for /api/health/worker; check the web server output for worker errors.

Still stuck

Collect docker compose ps, docker compose logs --no-color, and the run page's stop reason, and open an issue on GitHub. Remove API keys and secrets from anything you paste.

Clone this wiki locally