Skip to content

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.

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.

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.

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 again

Creating 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.

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 it
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.

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.

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/billing
error: 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/billing

The 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.

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.

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.

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 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 file

A 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:

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.

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.

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.

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.

unauthorized

The 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.
Terminal window
jiayang curl acme/hello
forbidden
403 Forbidden

The 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.

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.

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.

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.

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.

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.

The workspace has been suspended. Its owners can sign in to the dashboard to see why.

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.

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.

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.

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:

Terminal window
jiayang logs acme/hello --level error
10: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.

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:

Terminal window
jiayang logs acme/hello

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.

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.

hello null

The caller is a bypass token, which has no email. Use user.sub when there is no email. See What you get back.

egress to platform hosts is blocked

Your 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.

Fetch the Go SDK as jiayang.cloud/sdk, not by its GitHub path, with Go 1.25 or newer. See Go.

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 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.

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.

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.

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 directory

Start the agent in the project’s directory, or pass --roots. See Where a deploy may read from.

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.

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.