Troubleshooting
The errors you can meet while installing Ticketory, what causes each one, and how to fix it.
For the Full Stack package
Docker is not running
failed to connect to the docker API at unix:///…/docker.sock; check if the path is correct and if the daemon is runningOlder Docker versions say "Cannot connect to the Docker daemon" instead. Either way, the Docker engine is not started.
Open Docker Desktop
Start Docker Desktop and wait until it reports that the engine is running. On Linux, start the service:
sudo systemctl start docker.Check that Docker answers
Terminaldocker infoExpected result: It prints a Server section instead of an error.
Run your start command again
docker compose up --buildfor the full stack, or yourdocker buildanddocker runfor a single app.
A port is already in use
ports are not available: exposing port TCP 0.0.0.0:3031 … bind: address already in use
Bind for 0.0.0.0:3031 failed: port is already allocated
Error: listen EADDRINUSE: address already in use :::3031The first two lines come from Docker, the last from yarn dev. Another program already listens on 3030, 3031 or 8000: often an earlier run of Ticketory, or another project's dev server.
Find what holds the port
On macOS or Linux, with the port from the message:
Terminallsof -i :3031On Windows, in PowerShell:
Terminalnetstat -ano | findstr :3031Stop it
Close that program, or stop the earlier run: Ctrl+C in its terminal,
docker compose downin its folder, ordocker stopfor a container you started withdocker run. Then start Ticketory again.Or run Ticketory on other ports
With the full stack in Docker, create a file named
.envin theticketory-full-stackfolder, besidedocker-compose.yml, with the port you need.SITE_PORTmoves the website,ADMIN_PORTthe dashboard andAPI_PORTthe API; the addresses the apps use,CORS_ORIGINincluded, follow by themselves.ticketory-full-stack/.envADMIN_PORT=3041Then run the same command again. A failed start picks up where it stopped. Docker names one busy port at a time: if it stops on another one, add that one to
.envas well.Terminalinticketory-full-stackdocker compose up --buildExpected result: The app answers on its new port, here localhost:3041Local.
For a single app started with docker run, change the number on the left of -p, for example -p 3041:3031, and add the new address to the API's CORS_ORIGIN; set the API's STOREFRONT_URL to a moved website, or its FRONTEND_URL to a moved dashboard. Without Docker the ports are fixed in the files: the frontends' dev and start scripts in package.json pass -p 3030 and -p 3031, and the API reads PORT from its .env. Move the API together with PUBLIC_MEDIA_URL, PUBLIC_API_URL and both frontends' NEXT_PUBLIC_API_BASE_URL (the dashboard's NEXT_PUBLIC_WEBSOCKET_BASE_URL too); add a moved frontend's address to CORS_ORIGIN as well.
The Docker build fails
The first build downloads the base images and every dependency, then builds the images. It stops with "failed to solve" and the step that failed when something gets in the way.
- No connection or a timeout: the build needs internet access. Run the command again once you are online; finished steps are cached.
- No space left on device: free space in Docker Desktop, or check what Docker uses with
docker system df. - It fails at the same step every time: rebuild without the cache, then start.
- Compose rejects `docker-compose.yml` itself, naming `env_file`: your Compose is older than 2.24. Update Docker Desktop, or the Compose plugin on Linux.
ticketory-full-stackdocker compose build --no-cache
docker compose upFor a single-app package, add --no-cache to your docker build command.
A first start that seems stuck is usually still seeding the sample organisers. The website and the dashboard start only once the API reports itself healthy.
Yarn says its version is 1.22
This project's package.json defines "packageManager": "yarn@4…". However the current global version of Yarn is 1.22…Each app pins Yarn 4 through Corepack. This message means Corepack is not enabled yet, so the old global Yarn answered instead.
corepack enableThen run yarn install again. If your Node.js has no Corepack (Node.js 25 and newer leave it out), install it first with npm install -g corepack. npm and pnpm are not used: each app ships its own yarn.lock.
An install or a start fails on an older Node.js
Every app declares Node.js 22 or newer, and the Docker images run on Node.js 24. An older Node.js shows up as an install that fails to build a package, or an app that stops at start.
node -vIf it prints a version below 22, install Node.js 22 or newer, run corepack enable again (on Node.js 25 and newer, npm install -g corepack first), then delete the app's node_modules folder and run yarn install again. With Docker none of this applies: the images bring their own Node.js.
The API stops before starting
The API cannot start: JWT_SECRET is not set. Set it in back-end/.env to the output of: …
The API cannot start: JWT_SECRET is still the example value from .env.example, so anyone can sign a token for any account. …
The API cannot start: CORS_ORIGIN is not set. List the website and organiser dashboard origins, comma separated, …The API checks its settings before it starts and prints one line per problem. The causes:
- There is no `.env` file, or `JWT_SECRET` is empty. Create the file in the API's folder with
cp .env.example .env. - `JWT_SECRET` is still the example value with `NODE_ENV=production`. Anyone could sign a token with the public example, so a production start refuses it. Generate your own secret.
- `CORS_ORIGIN` is empty with `NODE_ENV=production`. List the website's and the dashboard's addresses, comma separated.
Generate a secret with:
node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"With Docker you do not need one: a container without a JWT_SECRET of its own, or with the example one, generates a secret and keeps it in the data volume.
The website has no events and nobody can sign in
→ No database at /data/database.sqlite. Creating the tables, no data (set SEED_DEMO_DATA=true for the demo).The database was created with tables only. That happens when the API container starts on a new volume without SEED_DEMO_DATA=true, when the full stack runs with SEED_DEMO_DATA=false in the .env beside docker-compose.yml, or without Docker when yarn db:sync ran instead of yarn seed. There are no events and no dashboard account to sign in with.
- Without Docker: run
yarn seedin the API's folder. It adds the sample organisers and their accounts to the tables you have. - Full stack in Docker: remove
SEED_DEMO_DATA=falsefrom the.env, thendocker compose down -vanddocker compose up. The data volume is created again with the sample data. - API container: remove the container, delete its volume, and run it again with
-e SEED_DEMO_DATA=true, as in Start over below. - For your own workspace instead of the sample data: set
OWNER_EMAILandOWNER_PASSWORD(at least 10 characters) and start the API again on that empty database. Its log then saysClean start: created the workspace; until they are set, it says which variables to set.
An existing database is never seeded again by the container, whatever SEED_DEMO_DATA says, so the variable only matters on a new volume.
Sign-in fails
- Check the account and the app. Staff sign in to the dashboard at port
3031(owner@example.test,manager@example.test,finance@example.test,checkin@example.test); the attendeeattendee@example.testsigns in on the website at port3030, never in the dashboard. Every seeded account uses the passwordDemoOnly!2026. - "Invalid email or password" in the dashboard answers a wrong password, an unknown email and a database with no accounts alike. Check the points below in turn.
- The database has no accounts when it was created with
SEED_DEMO_DATA=falseand no owner variables. See The website has no events and nobody can sign in, above. - "Too many sign-in attempts. Try again in a few minutes." Ten failed sign-ins from one address within 15 minutes lock the form for that address until the oldest failure is 15 minutes old. The website's sign-in counts the same way. Wait, or restart the API: the count is kept in its memory.
- "Network error. Check your connection and try again." means the dashboard cannot reach the API: see the CORS problem below.
- You changed a password and no longer have it: start over with fresh demo data, below.
Sign-in fails
- Check the account and the route. Staff sign in at
POST /api/auth/login(the seeded Owner isowner@example.test), attendees atPOST /api/customer-auth/login(attendee@example.test); every seeded account usesDemoOnly!2026. A failed sign-in answers401with the same message whether the email or the password is wrong. - No account works at all: the database was created without the sample data. Run
yarn seed, or start the container with-e SEED_DEMO_DATA=trueon a new volume. On your own install, the Owner is theOWNER_EMAILthe first start was given. - A `429` answer means one address failed 10 sign-ins within 15 minutes. It clears once the oldest failure is 15 minutes old, or when the API restarts.
RATE_LIMIT_LOGINchanges the number.
Sign-in fails
Your app signs in against the Ticketory API, so the account has to exist there. On a seeded API, staff sign in to the dashboard with owner@example.test and attendees to the website with attendee@example.test, both with DemoOnly!2026. If no account works, either the API was started without its sample data (run yarn seed in its folder, or start its container on a new volume with -e SEED_DEMO_DATA=true), or the app cannot reach the API: see the next problem.
The sign-up code or a ticket email never arrives
Until RESEND_API_KEY and MAIL_FROM are set in the API's .env, no email is sent. Every one (the 6-digit sign-up code, password resets, tickets, refund news and event reminders) is written to the API's log instead, starting with this line:
Mail not sent (no provider configured). Message follows:- Full stack in Docker: run
docker compose logs apiin the package folder and read the code from the newest message. - Without Docker: read it in the terminal that runs the API's
yarn dev. - API container:
docker logswith the container's id (docker pslists it). - To send real email, set both variables with a Resend key and a sender on a domain you verified with Resend, then restart the API.
Pages say "We could not load this right now"
We could not load this right now
Access to XMLHttpRequest at 'http://localhost:8000/api/…' from origin 'http://localhost:3041' has been blocked by CORS policyThe website shows the first line, and the dashboard says "Network error", when the browser cannot use the API. The browser's console shows the second line when a frontend runs on an address the API does not accept. The API answers browsers only from the addresses in CORS_ORIGIN, which defaults to http://localhost:3030 and http://localhost:3031. A website or dashboard moved to another port, or served on your own domain, is refused until it is listed.
CORS_ORIGIN=http://localhost:3030,http://localhost:3041
FRONTEND_URL=http://localhost:3041- Write each address exactly as the browser shows it, with the scheme and the port and without a trailing slash, comma separated. Then restart the API.
FRONTEND_URLis the dashboard's address, which its live notifications connect from. Move it with the dashboard.- For an API container, pass the same values with
-e, such as-e CORS_ORIGIN=http://localhost:3030,http://localhost:3041. - With the full stack in Docker you do not edit these:
SITE_PORT,ADMIN_PORT,SITE_URLandADMIN_URLin the.envbesidedocker-compose.ymlset them. - A frontend that cannot reach the API at all, because it is stopped or at another address, fails the same way without the CORS line. Check that localhost:8000/
api/ healthLocal answers, and that the frontend's NEXT_PUBLIC_API_BASE_URLnames that API.
An event page's tab shows only the site name in Docker
The website's own server reads the event's title for the browser tab, the first render of the home, event, venue and organiser pages, and the sitemap. Inside a container localhost is the container itself, so that server cannot reach an API on your computer at http://localhost:8000. The page still loads in the browser, which reaches the API directly.
Start the website container with the API's address as the server sees it:
docker run -p 3030:3030 -e API_INTERNAL_URL=http://host.docker.internal:8000/api --add-host=host.docker.internal:host-gateway ticketory-websiteThe full stack in Docker sets it for you (http://api:8000/api). Without Docker it is not needed.
Event pictures do not load
The seeded pictures are served by the API at /media/ticketory/…, and every picture address is built from PUBLIC_MEDIA_URL, which defaults to http://localhost:8000/media. A picture breaks when that address does not reach the API from the browser.
- The API runs on another port or domain. Set
PUBLIC_MEDIA_URLto the API's public address followed by/media, for example-e PUBLIC_MEDIA_URL=http://localhost:8010/mediaondocker run -p 8010:8000. With the full stack in Docker,API_PORTandAPI_URLset it for you. - The API moved after the first start. Restart it with the new
PUBLIC_MEDIA_URL: on start it points every stored picture at that address, and its log says "Pointed … stored media URLs at …". - Uploads to a bucket do not load.
R2_PUBLIC_URLmust be the bucket's public address, and the bucket must allow public reads. - Pictures load, but at full size. The frontends resize only pictures from the https hosts in
NEXT_PUBLIC_MEDIA_HOSTNAME(with Docker Compose,MEDIA_HOSTNAME), each written as the host alone and comma separated, such asapi.your-domain.com,pub-1234.r2.dev. Any other picture, such as one from an API on localhost, loads as it is. Rebuild the frontend after changing it.
Checkout offers only Test payment, or sends me back
Your reservation expired. Choose seats again.- Only Test payment is offered. The API has no live Stripe or PayPal key yet, so it offers the payment simulator, which marks an order paid without moving money. Add your keys to the API's
.envand restart it;PAYMENTS_SIMULATOR=onoroffthere overrides the choice. Free tickets show no payment method at all: they are confirmed with Get tickets. - "Your reservation expired". Chosen seats are held for the workspace's hold time, ten minutes in the sample data, on the server's clock. Choose the seats again to start a new hold.
- Checkout sends you back to the events. The browser tab holds no seats: checkout was opened in another tab (a hold belongs to the tab that made it), or the API no longer knows the hold. Choose the seats again.
- A Stripe or PayPal payment stays on "Confirming your payment". The page asks the API, which asks the provider, until the payment is confirmed. On a server, register the webhooks at
/api/webhooks/stripeand/api/webhooks/paypalso confirmations arrive at once, and check thatSTOREFRONT_URLandPUBLIC_API_URLare the addresses the buyer's browser uses.
A change to a frontend's .env does nothing
Every NEXT_PUBLIC_* value is compiled into the JavaScript the browser loads when the app is built. Changing the file changes nothing until the app is built again.
| How you run it | After changing a value |
|---|---|
yarn dev | Stop it and run yarn dev again. |
yarn build and yarn start | Run yarn build again, then yarn start. |
| Docker Compose | Run docker compose up --build. Set the value in the .env beside docker-compose.yml, not in the app's folder. |
docker build for one app | Build the image again with the value as a --build-arg. |
The venue studio or the assistant asks for a key
Generation needs a Google Gemini key
The assistant needs an AI provider keyThe assistant answers with Google Gemini, OpenAI or DeepSeek and turns on with the key of any one of them in the API's .env: GOOGLE_API_KEY, OPENAI_API_KEY or DEEPSEEK_API_KEY, the three its notice lists. The venue studio generates with Google only, so it needs GOOGLE_API_KEY whatever else is set. In .env.example the three lines are commented out, so a copied file starts cleanly and both features stay off, with nothing billed.
- Remove the
#in front of the line of each key you have, paste your real key over the example, and restart the API. - With Docker Compose the API reads
back-end/.envtoo, sodocker compose upagain is enough; a single API container needs--env-file .env -e NODE_ENV=productionon itsdocker run. - Reload the dashboard page after the restart.
- A model missing from the assistant's picker belongs to a provider whose key is not set: the picker lists only the models of providers with a key.
The assistant cannot answer with the chosen model
{provider} did not accept the API key, so {model} could not answer. Check that {variable} holds a valid {provider} key, then try again or pick a model from another provider.
The {provider} account behind this API key has no credit left, so {model} could not answer. Add credit or billing on that account, or pick a model from another provider.
{model} has no requests left on this API key right now. Pick a different model from the list above the chat, or try again in a few minutes.
{provider} has no room for {model} right now. Try again in a minute, or pick another model from the list above the chat.The assistant shows one of these in place of an answer when the provider behind the chosen model refuses the request, with Google, OpenAI or DeepSeek as the provider, the model's name, and the key's variable filled in. It runs only the model you picked: it never switches to another one by itself and never retries on its own, so fix the cause or pick another model in the list above the chat, then press Try again.
| The message says | Cause | Fix |
|---|---|---|
| did not accept the API key | The provider refused the key: mistyped, revoked or expired, or a key from another provider in that variable. | Put a valid key from that provider in the variable the message names, in the API's .env, restart the API and reload the page. |
| has no credit left | The provider account behind the key has no balance or billing set up. | Add credit or billing on that account with the provider, or pick a model from another provider. |
| has no requests left | The key's request allowance for that model is used up for now. | Wait a few minutes, or pick another model: allowances are often counted per model. |
| has no room for | The provider is overloaded at the moment. | Try again in a minute, or pick another model. |
In demo mode the assistant asks for the visitor's own key
Included with your purchase. Sign in to read, or open it in your download.
What a public demo's assistant says when its free messages are spent, and how to give visitors more.
The contact form or sign-up is refused
Too many requests were sent from this connection. Please wait a few minutes and try again.- Too many requests. Each address may send five contact forms in ten minutes, start ten sign-ups and ask for five password resets in the same time. While you test, wait, or restart the API: the counts live in its memory.
- A field the API does not know. Sign-up refuses a body with unknown fields with
400, naming them. This only happens to your own client or a changed form: send the fields the website sends. - Behind a reverse proxy, every visitor can look like the proxy and share one allowance: see the next problem.
Every visitor gets "Too many" behind a proxy
The sign-in, the sign-up, the contact form and the door scans are limited per address. Behind a reverse proxy, every visitor can look like the proxy's one address and share one allowance. Tell the API how many proxies stand in front of it:
TRUST_PROXY=1RATE_LIMIT_LOGIN changes the number of failed sign-ins allowed. Each door scanner may make 120 checks a minute. The counts live in the API's memory, so a restart clears them.
MySQL refuses to start or to seed
The API creates its tables in a database that already exists; it does not create the database itself. Create an empty database first, give its name to DB_DATABASE in the API's .env, then run yarn seed (or yarn db:sync for the tables with no demo data).
- Check the five
DB_*connection values and thatDB_TYPE=mysql. - With
NODE_ENV=productionthe running API never creates tables, so one of those commands has to run before the first start (yarn seed:prodoryarn db:sync:prodafteryarn build). - The Docker image creates the database by itself only on SQLite. On MySQL, run the seed or the schema command once yourself.
The API container cannot find its entrypoint
exec /usr/local/bin/docker-entrypoint.sh: no such file or directoryThe script has Windows line endings, which an editor or Git on Windows can add. The shipped API Dockerfile strips them while it builds, so this appears only with an image built from a changed Dockerfile. Keep its sed -i 's/\r$//' line, or save the script with LF line endings, then rebuild without the cache.
The unzip command fails or the folder looks different
caution: filename not matched: ticketory-…zipThat message means two ZIPs of the same package sit in the folder, such as an older download next to the new one, and the * in the command matched both. Delete the older ZIP and run the command again, or type the full file name instead of the *.
Run the next commands inside the folder the ZIP extracts to. If the unzip command is missing, extract it with your file manager instead; some tools add an extra folder named after the ZIP, so move into the inner one.
| Package | Folder | Contains |
|---|---|---|
| Full Stack | ticketory-full-stack | admin-dashboard, back-end, storefront, docker-compose.yml |
| Ticketing Website | ticketory-website | Dockerfile, package.json, .env.example |
| Organiser Dashboard | ticketory-dashboard | Dockerfile, package.json, .env.example |
| Backend API | ticketory-backend | Dockerfile, docker-entrypoint.sh, package.json, .env.example |
Start over with fresh demo data
This deletes your data
Everything you created locally is removed, and the sample organisers are seeded again.
With the full stack in Docker, from the ticketory-full-stack folder:
ticketory-full-stackdocker compose down -v
docker compose upWith a single API container, remove the container first (docker ps -a lists it, docker rm -f with its id removes it), then delete the ticketory-api-data volume and run it again. The full stack keeps its data in a different volume, ticketory-data, so this never touches it.
docker volume rm ticketory-api-data
docker run -p 8000:8000 -v ticketory-api-data:/data -e SEED_DEMO_DATA=true ticketory-apiWithout Docker, stop the API, then in its folder:
rm -f database.sqlite* && yarn seedIn PowerShell:
Remove-Item database.sqlite*; yarn seedRunning yarn seed again on a database you keep is not a reset: it adds no rows and never resets a password, but it puts the four built-in roles back to their default permissions. yarn db:reset drops every table, on SQLite and MySQL alike; run yarn seed after it.