Troubleshooting
real error messages and what to do.
Start with the log and the console's status screen: most problems show up in one of them.
01Reading the log
| How Kiln runs | Command |
|---|---|
npm start in a terminal | The terminal itself |
| systemd | journalctl -u kiln -f |
| PM2 | pm2 logs kiln |
| Docker | docker logs -f kiln |
Kiln's lines start with [kiln], the console's product pages with [console], the Admin Kit with [admin]. When the builder shows "Something went wrong on the server. The log has the details.", the full error is in the log. A quick health test: open <your address>/healthz; it answers {"ok":true,"version":"…"}.
The commands on this page are for a Linux or macOS terminal. On Windows, use WSL (Windows Subsystem for Linux) with Ubuntu, install Node.js inside it and type the commands there. Native Windows (without WSL) is not supported.
02The server does not start
- Wrong Node version. Run
node -v. Kiln needs 22.12 or newer. - Port in use (
EADDRINUSEin the log). Another program uses port 8787, often an older Kiln still running. Stop it (sudo lsof -i :8787shows which process) or set anotherPORTin.env. better-sqlite3errors such as "was compiled against a different Node.js version". This happens after upgrading Node. Runnpm rebuild better-sqlite3, ornpm ciagain.- Cannot open the database. The user running Kiln must be able to write to the
data/folder (sudo chown -R kiln:kiln /opt/kiln/datafor the systemd example). Cannot find module …/dist/src/server/index.js. The server is not built: runnpm run build.
03"The builder app is not built yet. Run: npm run build"
/app shows this when web/dist/ is missing. Run npm run build in the project folder and restart.
04The console owner cannot sign in (lost password or authenticator)
There is no reset by e-mail for the console owner, on purpose. On the server, in the Kiln folder:
node dist/src/server/recover-owner.js # lists the console members
node dist/src/server/recover-owner.js owner@your-agency.example # prints a new one-time password
node dist/src/server/recover-owner.js owner@your-agency.example --reset-2fa # also removes two-factor
Sign in at /admin with the printed password and change it. Every session of that account is signed out.
05The console setup code is lost
The code is printed only in the log, only while no console owner exists. Restart Kiln: each start prints a new code, and every printed code works until the owner is created. Once the owner exists, no code is printed.
06A saved key "cannot be decrypted"
The log shows [admin] secret "…" cannot be decrypted: was the secretKey changed?. ADMIN_SECRET_KEY is not the one the keys were saved with. Put the original key back in .env, or enter all keys again under Integrations (see Installation).
07Signing in to the builder
- "Wrong e-mail or password." Check the address. Ask an owner of the console for a Reset link (Users page), or use Forgot password (needs e-mail to be set up; the link is valid 2 hours).
- "Too many attempts. Wait 15 minutes and try again." More than 10 attempts in 15 minutes from the same address or for the same e-mail. Wait 15 minutes. If it happens to many people at once, see Behind a proxy below.
- Sign-in seems to work but you land on the sign-in page again. The cookie is marked Secure (because the public URL starts with
https://, orCOOKIE_SECURE=1) but you opened Kiln over plainhttp://, for examplehttp://<server ip>:8787. Browsers drop Secure cookies on http. Open the https address instead. - "This link is invalid, used or expired. Ask for a new one." Invite links last 7 days and work once.
- "Sign-up is by invitation." Access → Anyone can sign up is off. Invite the person instead.
09AI errors
These messages come from Kiln's provider layer and appear in the builder and in the console's AI usage page. Credits for a failed call are always refunded.
| Message | What to do |
|---|---|
| No AI key is configured. The operator adds one in the console (Integrations). | Add the key for the selected provider. |
| Anthropic rejected the API key. / OpenAI rejected the API key. / OpenRouter rejected the API key. | The key is wrong, revoked or from another account. Create a new one and save it again. |
| The Anthropic key has no access to this model. | Your Anthropic account or key cannot use this model. Choose another model or check the account. |
| Anthropic does not know this model id. Check AI → Model in the console. | Typo in AI → Model id. Use e.g. claude-opus-5-5. |
| Set the OpenAI model id under AI → Model in the console. (or OpenRouter) | These providers have no default model. Enter one. |
| OpenAI error 404: … / OpenRouter error …: … | Usually an unknown model id, or a model without JSON-schema output. |
| Anthropic rate limit reached. Wait a minute and retry. | Your provider account's rate limit. Wait, or raise the limit with the provider. |
| The answer was cut off (max tokens). Try fewer pages or sections at once. | The page was too long for one answer. Remove sections from that page in the plan, or split the page. |
| The model declined this request. Rephrase the brief and try again. | Change the brief's wording. |
| The model returned text that is not valid JSON. Retry. | Retry. If it repeats with OpenAI or OpenRouter, pick a model with structured output. |
| Could not reach Anthropic. Check the server's internet connection. / Could not reach OpenAI. | The server cannot reach the provider: DNS, firewall or an outage. |
| Not enough credits: this needs N, the workspace has M. | Add credits on the Workspaces page, or let the client buy a pack. |
| Too many AI requests in a minute. Wait a moment and try again. | One person started more than 30 AI actions in a minute (each translated page counts). Wait a minute. |
| Set the endpoint URL under AI in the console. | The provider is Any OpenAI-compatible endpoint but AI → Endpoint URL is empty. |
| Could not reach The AI endpoint. / The AI endpoint error 404: … | Wrong endpoint URL (it must end before /chat/completions), the server cannot reach it, or the model id is unknown there. For Ollama on another machine, use that machine's address, not localhost. |
10Rebuild from URL errors
| Message | What to do |
|---|---|
| Enter a web address like https://example.com. | The address could not be read. Paste the full address. |
| The domain … does not resolve. | Typo, or the domain has no DNS record. Open it in a browser first. |
| That address is not on the public internet. | The address points to a private, local or reserved network. Only public sites can be read. |
| Only http and https addresses can be imported. / Only the standard web ports can be imported. / Addresses with a user name or password cannot be imported. | Use a plain https:// address without a port (80, 443 and 8080 are allowed) or credentials. |
| Could not reach …. | The site did not answer within 10 seconds, refused the connection, or one of its addresses is private. |
| … answered 403. (or another number) | The site refused the request, often a firewall or bot protection. Copy the text into the brief by hand. |
| That address is not a web page. | The address returns a file or data, not HTML. Paste the address of a page. |
| Too many redirects. | More than three redirects. Paste the final address from the browser's address bar. |
| No readable text on that page. Sites built only with JavaScript cannot be read. | The page builds its text in the browser. Copy the text into the brief by hand. |
| Too many imports in a minute. Wait a moment. | More than 10 imports by one person in a minute. |
11Cloudflare errors
Cloudflare's own messages are shown after Cloudflare: together with Cloudflare's error code.
- "Cloudflare is not set up…" The account id (Hosting) or the API token (Integrations) is missing, or the account id is not 32 hex characters.
- No workers.dev subdomain. A new Cloudflare account has none. The token test and every deploy fail. Open Workers & Pages in Cloudflare and set up the subdomain (step 1).
- Authentication or permission errors. The token lacks a permission, or the account is not in its Account Resources. Recreate it with the permissions listed in Publishing to Cloudflare.
- "No zone for … in this Cloudflare account. Add the domain to Cloudflare first." The custom domain must be a zone in the same Cloudflare account, and the token's Zone Resources must include it.
- "Publish the site to Cloudflare first." Deploy once before attaching a Cloudflare domain.
- "The … plan does not include Cloudflare publishing." Turn on Cloudflare publishing for that plan.
12Stripe
The webhook's answer is visible in Stripe → Developers → Webhooks → your endpoint → the event.
| Webhook answer | Cause |
|---|---|
| 503 "Webhook secret not set" | Add the signing secret under Integrations. |
| 400 "Missing Stripe-Signature header" | Something other than Stripe called the URL, or a proxy strips the header. |
| 400 "Stripe signature does not match" | The signing secret belongs to another endpoint, or to test mode while the event is live (or the reverse). Copy the secret of this endpoint again. A proxy that changes the request body also causes it. |
| 400 "Stripe event timestamp outside the tolerance" | The server's clock is more than 5 minutes off. Turn on time sync (timedatectl set-ntp true). |
In the builder: "Online payments are switched off." (turn on Sell plans and credit packs), "Stripe is not set up." (no secret key), "The … plan has no Stripe price id yet." (add it on the Plans page), "No Stripe customer for this workspace yet." (the portal works after the first payment), "Stripe: …" (Stripe's own message, for example an invalid price id, or the customer portal not activated in Stripe).
13Form messages do not arrive
- Public URL. Forms post to
<public URL>/f/<site id>. If the public URL is empty or wrong, the form posts to the wrong place. Set it. Sites on the built-in hosting pick it up at once; Cloudflare deployments and exported ZIPs contain the address from the time they were made, so deploy or export them again. - Reachable over https. Open
<public URL>/healthzfrom outside your network. - Cross-site request. Sites on other domains (Cloudflare, exports, custom domains) first send an
OPTIONSrequest. Your proxy or firewall must passOPTIONSandPOSTto/f/unchanged. - Rate limit. At most 5 messages per 10 minutes per sender and site; then "Too many messages. Try again in a few minutes." See Behind a proxy above.
- Rejected input. The form needs a valid e-mail ("Enter a valid e-mail address.") and a message ("Write a message."), and at most 20,000 characters.
- Honeypot. The form has a hidden field that only bots fill in. Such messages are answered with success and dropped. A browser extension that fills every field can trigger this.
- E-mail notices. Messages are always stored in the Forms tab. The e-mail to workspace owners needs the Resend key, the sender (E-mail section) and E-mail workspace owners about new form messages.
14The form webhook does not fire
The webhook is called after a message is stored; failures are not shown or logged, and not retried. Check:
- The message arrived in the Forms tab. If not, see Form messages do not arrive above: the webhook only runs for stored messages (not for honeypot or rejected ones).
- The address starts with
https://. Any other address is removed when the site is saved; reload the editor and look at the field. - The site was published after the webhook was set. A published site uses the webhook of its published version.
- No redirect. Kiln does not follow redirects. Use the exact address your service gives you.
- Public address, standard port. Addresses that resolve to a private network, or use a port other than 443 or 8080, are refused.
- Answer within 5 seconds. A slower endpoint is cut off.
- Test the endpoint from the server:
curl -X POST -H 'content-type: application/json' -d '{"fields":{}}' https://your-webhook-address.
15Analytics do not load
- The consent banner is on. Google Analytics, Tag Manager and the Meta Pixel load only after a visitor clicks Accept. A visitor who clicked Decline is not asked again in that browser. To test, open the site in a private window and accept, or clear the site's data in the browser. Plausible does not wait for consent.
- The id was dropped. An id in the wrong format is removed when the site is saved: GA4
G-…, Tag ManagerGTM-…, Meta Pixel digits only, Plausible the bare domain. Reload the editor and look at the field. - Not published yet. Analytics run on the published site only, never in the editor canvas. Publish after you change them.
- Astro export. The Astro project does not include analytics; add them in the project yourself.
- Ad blockers in the visitor's browser block these scripts. Test in a browser without one.
16E-mails are not sent
Check the E-mail can be sent status check. The Resend Test may say "Resend rejected the key (a sending-only key cannot list domains; it may still send)": a sending-only key can still work. The sender's domain must be verified in Resend.
17A custom domain stays "Waiting for DNS"
- "No TXT record _kiln-verify.<domain> with the expected value yet. DNS changes can take up to an hour." The TXT record is missing, has a typo, or has not spread yet. The record's name is
_kiln-verify.<domain>(some DNS panels add the domain themselves: then enter only_kiln-verify.www), and its value is exactlykiln-verify=<token>from the instruction. Check it withdig TXT _kiln-verify.www.bakery.example, wait, and press Verify again. - "Set the public URL of this server in the console first." Built-in hosting domains need Hosting → Public URL.
- Until it is verified, the domain is not served: visitors on it do not see the site.
18A published site address shows the builder or "Not found"
- The sites domain in Hosting must be exactly the part after the site address, e.g.
sites.your-agency.example. - The site must be published. An unknown site address under
/s/answers "Not found". - Your proxy must pass the original
Hostheader (Caddy does by default; in nginx useproxy_set_header Host $host;).
19Uploads fail
"Only PNG, JPEG, WebP and GIF images." (SVG and other files are refused), "Images up to 8 MB.". If your proxy has a smaller body-size limit (nginx's default is 1 MB), raise it, e.g. client_max_body_size 10m;.
20A client's changes are "Not saved"
A member with the client role sees "Your role can change texts and images. Ask the agency to change sections, layout or design." when a save contains anything besides text and image changes, for example a page address, a site setting in the Page tab, or a link address. Undo that change (⌘Z) and the next save goes through, or ask an editor or owner to make it.
21"Missing request header"
The builder sends an x-kiln header with every change. A proxy or firewall that strips unknown headers causes this
- Let the header through.