Troubleshooting
Each entry starts with the message you see, then says what it means and how to fix it. Messages from the CLI start with error:. Add --json to any command to get the error as JSON, with a code such as unauthenticated, not_found or plan_limit.
Signing in
Section titled “Signing in”Not logged in
Section titled “Not logged in”error: not logged in: run `jiayang login`This computer has no session. Run jiayang login. Each config directory has its own session, so check --config-dir and JIAYANG_CONFIG_DIR if you set either.
Your session has ended
Section titled “Your session has ended”error: your session has ended (signed out, revoked or expired): run `jiayang login`The session expired, was signed out or revoked from the dashboard’s Sessions page, or was replaced by a newer jiayang login on this computer. Run jiayang login again. A CLI session lasts 30 days.
Terms of service
Section titled “Terms of service”error: the terms of service need agreeing to first: sign in to the dashboard once at https://app.jiayang.cloud, accept them, then run this againCreating a workspace, an app or a token waits until you have accepted the terms in force. Only you can, in a browser. Sign in to the dashboard, accept them, and run the command again. When new terms are published ahead of the day they take effect, the dashboard offers them early; accept them then and nothing waits on the day.
Couldn’t reach the platform
Section titled “Couldn’t reach the platform”error: couldn't reach https://api.jiayang.cloud: ...The CLI couldn’t connect. Check your network. On a network whose proxy inspects TLS (Zscaler, Netskope and the like), the rest of the message is usually a certificate error: point JIAYANG_CA_FILE at the proxy’s CA bundle, in PEM. The CLI uses SSL_CERT_FILE when that isn’t set, and trusts either on top of its own roots.
A file it can’t use stops every command, rather than failing later with a certificate error:
error: /Users/you/corp.pem (named by JIAYANG_CA_FILE) has no certificates in itWorkspaces and apps
Section titled “Workspaces and apps”App not found
Section titled “App not found”error: app acme/hello not found. `jiayang app list acme` shows the apps there that you can see, and `jiayang deploy acme/hello <dir> --create` makes it. If acme isn't a workspace you're in, `jiayang workspace list` shows the ones you are.There’s no such app, or you can’t see it. The platform answers the same for both on purpose. Check the name with jiayang app list acme. Apps you have no access to don’t show up there: ask an owner to share it.
Workspace not found
Section titled “Workspace not found”error: workspace acme not found, or you're not in it. `jiayang workspace list` shows yours, and `jiayang workspace create <slug>` makes a new one.Workspace slugs are exact. jiayang workspace list shows the ones you belong to. A deleted workspace’s slug stays taken for 30 days, so it can’t be made again straight away.
Plan limits
Section titled “Plan limits”error: This workspace has 3 apps, which is all the Free plan allows. Delete one, or upgrade: Team allows 25 apps. Billing: https://app.jiayang.cloud/w/acme/billingerror: The Free plan runs JS and static apps only. Container apps (Python, Go, Rust, Node servers) need a paid plan: Team runs container apps. Upgrade the workspace at https://app.jiayang.cloud/w/acme/billingThe workspace’s plan doesn’t allow one more of something. The message names the limit and the plan that lifts it. An owner can upgrade on the billing page it links to. See Limits and Plans and billing.
Suspended workspace
Section titled “Suspended workspace”jiayang deploy checks the workspace before it builds anything, and stops:
error: acme is suspended by the platform, and nothing can be deployed to it until that's lifted. Contact support.Any other command that would change something fails with:
error: This workspace has been suspended by the platform. Nothing can be deployed or changed until it is lifted; contact support. The dashboard, billing, export and deletion still work.Its apps don’t answer either. Contact support. A workspace suspended over a payment dispute has a message of its own, which says when it starts again.
Deploying
Section titled “Deploying”It could be either kind
Section titled “It could be either kind”error: this has both a Dockerfile and wrangler.jsonc, so it could be either kind. Say which with --kind=container or --kind=worker.The directory has signs of two kinds of app. Pass --kind, or set kind in jiayang.json so every deploy agrees. See How deploy decides what to build.
Nothing says what this is
Section titled “Nothing says what this is”error: nothing here says what this is. Looked for: - a Dockerfile - a wrangler config (wrangler.jsonc, wrangler.json, wrangler.toml) - package.json with a Cloudflare adapter, a static builder or a server - requirements.txt, pyproject.toml or Pipfile - go.mod - Cargo.toml - index.html - an entry point (index.js, index.mjs, src/index.js, src/index.mjs, dist/index.js, dist/index.mjs, src/index.ts, index.ts, src/worker.ts, worker.ts)
Say which it is with --kind, or write it in jiayang.json.Check you pointed the deploy at the right directory. If you did, say what it is with --kind or jiayang.json.
A file looks like a credential
Section titled “A file looks like a credential”A static site’s deploy says:
error: .env looks like a credential, and everything here is served to everyone who can open the app. Take it out of the directory you're deploying.A Worker’s names the whole path:
error: /Users/you/hello/.env looks like a credential, and the app shouldn't hold one: store it with `jiayang secret set` and remove the fileA container’s lists every file the build would copy into the image:
error: these look like credentials and the build would copy them into the image: .env. The app shouldn't hold them: store them with `jiayang secret set`, then delete the files or list them in .dockerignore.The deploy refuses to upload .env files, private keys and anything else that looks like a credential. Move each value to where it belongs, then remove the file from the directory:
- A credential your app sends to another service goes in
jiayang secret set. See Secrets and outbound calls. - Plain configuration goes in
jiayang env set. See Environment variables.
A variable looks like a credential
Section titled “A variable looks like a credential”error: STRIPE_SECRET_KEY wasn't set because its name says it's a credential. Credentials belong in `jiayang secret set`. If this really is configuration, pass --allow-sensitive-name.Store credentials with jiayang secret set, so your app never holds them. A webhook’s signing secret goes on the path’s verifier in the dashboard instead, and the platform checks each delivery with it: see Public paths and webhooks. If your app really has to hold the value itself, pass --allow-sensitive-name to jiayang env set. A value the platform is sure is a credential, like a live API key or a whsec_ signing secret, is refused even then.
A name is set by the platform
Section titled “A name is set by the platform”error: JIAYANG_APP_ID is set by the platform. Pick another name.Names starting with JIAYANG_, CF_ or CLOUDFLARE_, and a few others such as PORT and DB, are reserved. Pick another name.
Cloudflare refused the app’s code
Section titled “Cloudflare refused the app’s code”error: Cloudflare refused the app's code: Uncaught Error: No such module "jiayang.mjs". imported from "index.js"Cloudflare loads a Worker as it goes up, and refuses one that doesn’t parse, throws as it starts, imports a module that isn’t there, is too big, or handles no requests. The rest of the message is Cloudflare’s, about your code. A Worker with no build goes up as the files in its directory, so every module it imports has to be one of them.
Fix it and deploy again. The version is listed as failed, and the one before it kept serving. The audit log has the same reason, under app.deploy_failed.
Reaching your app
Section titled “Reaching your app”A browser is sent to sign in
Section titled “A browser is sent to sign in”A page load with no session is redirected to the dashboard’s sign-in page and back. That’s expected the first time someone opens an app, and again after their session ends. After signing in they land on the page they asked for.
401 unauthorized
Section titled “401 unauthorized”unauthorizedThe edge or your app sent this, and they look the same.
The edge sends it when it doesn’t know who is calling: no session on this app’s host, a session that has ended, or no bearer token at jiayang-apps.cloud/<workspace>/<app>/. A request from a page’s script (not a page load) gets this rather than a redirect. Reload the page to sign in again. A script needs a bypass token.
The SDKs refuse a caller with the same unauthorized. If the audit log has an allow for the request, the edge let it through and the 401 is your app’s. Its log says why, such as couldn't fetch the identity keys or invalid identity token.
jiayang curl works out which for you. When your CLI session has ended, it fails with the session has ended error. When the session is fine, it adds:
Your session is fine, so this 401 is the app's own answer, not the platform's.403 forbidden
Section titled “403 forbidden”jiayang curl acme/helloforbidden403 ForbiddenThe caller is signed in and a member of the workspace, but the app isn’t shared with them. Check with jiayang access acme/hello, and share it if it should be. A new share takes effect within 60 seconds.
In a browser, a page load shows a page titled You don’t have access to this app, with the address the person is signed in as. If the app was shared with another of their addresses, they sign out in the dashboard and open the link again.
A browser also gets 403 for a POST, PUT, PATCH, DELETE or WebSocket sent from another site, even when it’s signed in. Send the request from the app’s own pages.
A bypass token gets 403 for any app except the one it was made for.
404 not found
Section titled “404 not found”No app answers at that address, nothing is deployed to it yet, or its workspace was deleted. Check the name with jiayang app list acme. With a bypass token, the same cases get 403.
Someone who isn’t a member of the workspace gets 404 for an app that isn’t shared with them, too, so they can’t tell it from one that isn’t there. If it should be shared with them, check with jiayang access acme/hello. The audit log records it as no_grant.
In a browser, a page load shows a page titled Nothing to open here, with the address the person is signed in as. A share sent to another of their addresses is the usual cause: they sign out in the dashboard and open the link again as that one.
Paused for the month (402)
Section titled “Paused for the month (402)”A page titled “Paused for the month” means a Free workspace has used what its plan includes this month: 200,000 requests, twice what the plan includes, or, from 1 November 2026, 50,000,000 database rows read or 100,000 written. From that day every request its apps are sent counts, public paths and webhook deliveries included. The app answers again when the month starts over, or once an owner moves the workspace to a larger plan. See Limits.
Usage can’t be checked (402)
Section titled “Usage can’t be checked (402)”From 1 November 2026. The workspace is on Free, and the platform hasn’t been able to measure its database rows for two hours, usually because Cloudflare’s analytics are down. Only apps with a database stop; they answer again once the rows can be measured. Nothing needs doing on your side. The audit log records it as rows_unmeasured.
Databases over the limit (507)
Section titled “Databases over the limit (507)”From 1 November 2026. The workspace’s databases together hold more than the plan’s database limit: 1 GB on Free, 2 GB on Team or 10 GB on Business. Apps with a database answer only the workspace’s owners and admins, signed in as themselves, so they can delete rows through the app. Everyone else, bypass tokens, public paths and webhooks get this page, and the audit log records database_full. If the databases grow further or reach 10% over, owners and admins get it too, recorded as database_hard.
An owner or admin can get back under with jiayang db exec, which takes a DELETE, DROP TABLE or DROP INDEX while the workspace is over, or by exporting data and removing it, or by upgrading. The apps answer again within a minute of a measure that finds the databases under. See the database limit.
This app needs a paid plan (402)
Section titled “This app needs a paid plan (402)”The app runs in a container, and its workspace is on the Free plan, which doesn’t run container apps. It answers once an owner moves the workspace to Team or Business.
App unavailable (403)
Section titled “App unavailable (403)”The workspace has been suspended. Its owners can sign in to the dashboard to see why.
Signed in twice
Section titled “Signed in twice”You were signed in twice here, which can mean another app set a cookie it shouldn't have. We've cleared it: reload the page.The request carried two session cookies for this host, and the edge refuses rather than pick one. Its answer clears any jiayang_session cookie set for a parent domain, the kind another app could plant. It never clears the edge’s own __Host-jiayang_session cookie.
Reload the page. If the message comes back, clear this site’s cookies in your browser and open the app again.
429 too many requests
Section titled “429 too many requests”On a public path with no verifier, one client can only send an app so many requests a minute. Slow down and retry.
If they come from a webhook provider or a Worker, others send from the same addresses, and what they send to the same path counts toward your minute too. If the sender signs its deliveries, give the path a verifier: a verified path isn’t under this limit.
The platform also counts how many names with no app behind them one client has it look up in a minute. Those don’t count toward real apps, so what someone else sends from the same address to made-up names can’t hold up your deliveries. An app made in the last few minutes may be counted with them, and get the answer a name with no app gets, until the platform’s list of apps has it. Each real app gets a count of its own, which a client only reaches while the app’s workspace can’t serve requests or the platform can’t look the app up, since every request is then a lookup. See Public paths and webhooks.
A webhook delivery is refused
Section titled “A webhook delivery is refused”On a path with a verifier, every check a delivery fails answers the provider 401 unauthorized, so the provider’s log won’t say which. The path’s row under Sharing, and jiayang app public-paths acme/hello, say how the newest delivery went, and to anyone who can change the verifier, which check it failed. The workspace’s owners and admins have every delivery in the audit log, with the check after webhook_refused:
| Check | What to do |
|---|---|
signature |
The secret on the verifier isn’t the one the provider signs with. Rotate it to the provider’s current one. |
missing_header, malformed |
The sender isn’t the provider the verifier names, or for Standard Webhooks, sends the other set of header names. Replace the verifier. |
stale |
The delivery was signed longer ago than the verifier allows. Raise How old a delivery may be, up to 10 minutes. |
method |
Only POST reaches a verified path. |
replayed |
A copy of a delivery already let through. Nothing to do. |
unconfigured |
The verifier, its secret or the app’s public paths changed a moment ago. The provider’s retry is checked against what’s there now. |
rate_limited |
A 429: the workspace had too many verified deliveries, or this path more than its share of them. Most providers retry; redeliver GitHub’s from the webhook’s Recent Deliveries. |
too_large |
A 413: the body is over 1 MiB, more than a verified path takes. |
A delivery sent to the app’s own address, https://hello--acme.jiayang-apps.cloud/hooks/stripe, never reaches the path: that address asks everyone to sign in. Give the provider the address the dashboard shows, https://jiayang-apps.cloud/acme/hello/hooks/stripe.
A delivery was let through, but the provider shows 401
Section titled “A delivery was let through, but the provider shows 401”The path’s row says let through, and the log shows an allow with the provider as the caller:
20:58:02 ALLOW webhook:stripe POST /acme/hello/hooks/stripe -Then the 401 came from your app. Most often a check for people, like app.use(requireUser()) or jiayang.Middleware, runs before the webhook route and refuses the delivery’s token as invalid identity token. Mount the webhook route before it: each SDK’s page shows where. If the route calls requireWebhook and logs not a webhook this route takes, it names a different provider from the path’s verifier.
The platform doesn’t hold on to a delivery your app didn’t answer with a 2xx, so the provider’s retry gets through once the app is fixed.
500, or Cloudflare’s error 1101
Section titled “500, or Cloudflare’s error 1101”The platform let the request through and your app failed. Error 1101 means a Worker threw an exception it didn’t catch. Its log has the exception and the stack:
jiayang logs acme/hello --level error10:04:31 ERROR v3 GET /pay TypeError: Cannot read properties of undefined (reading 'id') at charge (index.js:42:17)The line says which version was live. If the newest deploy is what broke it, jiayang rollback acme/hello 2 puts the one before back while you fix it. See Logs.
502 The app didn’t answer
Section titled “502 The app didn’t answer”A container app’s container went away before it answered: it crashed, ran out of memory, or was stopped mid-request. What it printed on the way down is in its log:
jiayang logs acme/hello503 service unavailable
Section titled “503 service unavailable”Something the edge needs to decide didn’t answer: it couldn’t check access, couldn’t check a webhook delivery’s signature in time, or couldn’t record the request in the audit log. It refuses rather than let the request through. Try again shortly.
A container app answers The app is starting. Try again in a few seconds. with a Retry-After when its container isn’t up within 20 seconds. A browser loading a page waits 3 seconds instead, then gets a short page saying the same, which reloads itself every 5 seconds. The container keeps starting, so the next try usually gets through. Right after an app’s first deploy, Cloudflare can take several minutes to get its container ready, and until then every request gets this answer.
If it never stops, check the app listens on 0.0.0.0 and on the port in PORT. A server listening on 127.0.0.1, or on a port of its own, is never reached, and looks just like one still starting. jiayang logs shows what it printed as it started.
Inside your app
Section titled “Inside your app”Your app refuses everyone with 401
Section titled “Your app refuses everyone with 401”The SDK couldn’t verify the token. Log the error’s message: it says why. jiayang logs acme/hello shows what the app logged.
| Message | What to do |
|---|---|
JIAYANG_APP_ID, JIAYANG_IDENTITY_ISSUER and JIAYANG_JWKS_URL must be set |
The app isn’t running on the platform or under jiayang dev, or it reads its environment in a way that loses them. On Workers, pass the handler’s env to the SDK. |
no identity token |
The request didn’t come through the edge. Locally, open http://127.0.0.1:8787, not your app’s own port. |
invalid identity token |
The token failed a check. Check the app reads the platform’s JIAYANG_APP_ID and not a value of its own. |
not a webhook this route takes |
A verified path’s delivery reached a route that names another provider. Name the provider the path’s verifier checks. |
no provider named |
The route calls requireWebhook with no provider, so it refuses everything. Name the one that sends to it. |
couldn't fetch the identity keys |
The app couldn’t reach JIAYANG_JWKS_URL. In a Rust container, see TLS in a container. |
A request that carries only X-Jiayang-Email is refused on purpose. The SDKs read only the signed token. See SDK overview.
In Go, the package-level functions read the variables once. If they were missing on the first request, restart the process.
On a webhook route, invalid identity token also means the token is a person’s, not a webhook’s: the request came in through a signed-in session or a bypass token rather than a verified public path, or it came through jiayang dev.
The email is null
Section titled “The email is null”hello nullThe caller is a bypass token, which has no email. Use user.sub when there is no email. See What you get back.
Calls to the platform are blocked
Section titled “Calls to the platform are blocked”egress to platform hosts is blockedYour app’s outbound request went to the dashboard, the API, the edge or another app on the platform, which apps can’t reach. The only platform address an app can fetch is the key set at JIAYANG_JWKS_URL.
go get fails with a module path mismatch
Section titled “go get fails with a module path mismatch”Fetch the Go SDK as jiayang.cloud/sdk, not by its GitHub path, with Go 1.25 or newer. See Go.
jiayang dev
Section titled “jiayang dev”The port is in use
Section titled “The port is in use”Making a signing key for this run ...error: couldn't listen on 127.0.0.1:8787: Address already in use (os error 48)Something else holds port 8787. Stop it, or pass --port.
Nothing is listening
Section titled “Nothing is listening”Nothing is listening on 127.0.0.1:8788 yet (Connection refused (os error 61)).Your app hasn’t started, crashed, or listens on another port. Make it listen on PORT, or pass --app-port to match the port it uses.
421 Misdirected Request
Section titled “421 Misdirected Request”jiayang dev only answers requests for localhost, 127.0.0.1, [::1] and names ending in .localhost. Open http://127.0.0.1:8787, not another name that points at your machine.
A webhook route refuses everything
Section titled “A webhook route refuses everything”jiayang dev signs tokens for people only, so a route that calls requireWebhook refuses every request with invalid identity token. Try webhooks on the platform: deploy, add a verifier to the path, and send a test delivery from the provider. See Public paths and webhooks.
See Local development.
Agents
Section titled “Agents”The agent says it’s not logged in
Section titled “The agent says it’s not logged in”not logged in: ask the person to run `jiayang login` in a terminal. This server acts as whoever the CLI is signed in as, and has no credential of its own.Run jiayang login in a terminal. The agent uses the CLI’s session, and picks it up on its next call.
The agent won’t deploy from your home directory
Section titled “The agent won’t deploy from your home directory”this session works in your home directory, a directory above it, or the filesystem root, which holds too much to deploy from: start it in the project's own directoryStart the agent in the project’s directory, or pass --roots. See Where a deploy may read from.
A deploy times out
Section titled “A deploy times out”A deploy is one tool call, and a build can take minutes. Give your client’s tool calls 900 seconds. See Give a deploy time.
Still stuck
Section titled “Still stuck”Look at what the platform saw. jiayang audit acme --app hello lists every request it allowed or refused, and why, for workspace owners and admins. jiayang watch acme/hello shows them as they happen. See Audit log.
Then look at what your app saw. jiayang logs acme/hello shows what it printed and the exceptions it threw. See Logs.