Troubleshooting
Find the problem you are seeing, and the fix. Every refusal from Spryloom says what happened and what to do; this page is for when that isn't enough.
Tip
spry checkshows what Spryloom sees in your folder and whether it could publish it, without changing anything. It is often the fastest first step.
Installing and signing in
Getting the spry command onto your computer, and signing in the first time.
npm install -g spryloom says EACCES: permission denied
npm error code EACCES
npm error path /usr/local/lib/node_modules/spryloom
WhyNothing is wrong with the package. Your Node keeps global packages in a folder only the administrator can write to. This is common when Node came from the installer on nodejs.org.
WarningDon't use
sudo. It runs install scripts as the administrator and leaves files behind that cause the same error later.
FixSkip the install. npx spryloom is the same command and needs nothing
installed, so put it wherever you see spry:
npx spryloom login --email you@yourcompany.com
npx spryloom publish .
To install it for good, give npm a global folder in your home directory, once:
mkdir -p ~/.npm-global
npm config set prefix ~/.npm-global
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
npm install -g spryloom
On Linux, or if your shell is bash, use ~/.bashrc in place of ~/.zshrc. A
Node version manager such as nvm or fnm avoids this too, because it keeps Node
in your home directory.
spry: command not found
Whyspry isn't installed, or npm's global folder isn't on your PATH.
FixInstall it with npm install -g spryloom. If it is installed and still
not found, npm prefix -g says where npm put it: add that folder's bin to
your PATH, or use npx spryloom instead.
If spry runs and prints nothing at all, you have an old version. Install it
again.
npm says the engine is unsupported, or spry fails straight away
Whyspry needs Node 20.19 or newer.
FixRun node --version. If it is older, install a current Node from
nodejs.org, or with nvm or fnm.
The sign-in email doesn't arrive
WhyIt is usually in spam, or the address has a typo.
Fix
- Look in spam or junk, and search for "Spryloom".
- Check the address you typed for a mistake.
- The link lasts 15 minutes and works once. If it has run out, sign in again for a new one.
After 5 attempts in an hour, Spryloom stops sending sign-in emails to that address for a while, and says so. Wait, then try once.
The code on the page is different from the one I was shown
WhyThat link belongs to a different sign-in, perhaps one you started earlier.
FixClose the page and don't confirm. Use the email from the sign-in in front of you, or start again.
Your agent and Spryloom
Claude Code, Codex, Cursor and other agents. Setup for each one is on using Spryloom from your agent.
The Spryloom tools don't appear
WhyMost agents read their MCP servers only when they start, or the server failed to start.
FixRestart the agent after adding the plugin or the server. Then check:
| Agent | Where to look |
|---|---|
| Claude Code | Type /mcp. If spryloom is listed as failed, select it to see why. If it isn't listed, run the two /plugin commands again, or claude mcp add spryloom -- npx -y spryloom mcp. |
| Codex | codex mcp list should show spryloom. |
| Cursor | Cursor Settings → MCP shows whether spryloom started. If it didn't, see the next problem. |
spawn npx ENOENT, or the server fails to start in Cursor
WhyAn agent opened from the Dock or Start menu may not see the same PATH
as your terminal, so it can't find npx. This is common when Node came from
nvm.
FixGive the full path. In a terminal, run which npx, then put what it
prints in place of npx in the agent's MCP settings:
{
"command": "/Users/you/.nvm/versions/node/v24.20.0/bin/npx",
"args": ["-y", "spryloom", "mcp"]
}
Every tool says Spryloom is not signed in
WhyNobody has signed in on this machine yet. This is expected the first time.
FixGive the agent your email address when it asks, and confirm the
emailed link. Or run spry login (or npx spryloom login) in a terminal; the
agent picks it up on its next call.
The agent published to the wrong account
WhyThe agent uses whoever this machine is signed in as, and never switches account by itself.
FixIn a terminal, run spry logout, then spry login with the right
address.
The agent says "Spryloom is not connected" and stops
WhyThe tools aren't loaded, and the agent didn't fall back to the command line.
FixTell it to use npx -y spryloom, or run npx spryloom publish .
yourself in the app's folder. Then fix the setup above so the tools are there
next time.
The publish failed
Good to knowA publish that fails changes nothing. The new version isn't started until it is built, and the old one keeps serving until the new one is answering.
"Build failed: cannot find 'pg'."
WhyThe app imports a package that isn't in package.json.
FixAdd it, then publish again:
npm install pg
spry publish .
"Build failed at step: …"
WhyYour app's own build command failed. The last of its output is shown above the message; it is your app's output, not Spryloom's.
FixFix the cause it shows and publish again.
"The app built, but it isn't accepting requests."
WhyThe app started but never answered on the port it was given. Almost
always it listens on a fixed port, or on 127.0.0.1, which can't be reached
from outside.
FixListen on the port Spryloom gives, on 0.0.0.0:
server.listen(process.env.PORT || 3000, '0.0.0.0');
"This app asks for a database, and this deployment has none to give it."
WhyThe manifest declares postgres: true and there is nowhere to create
one. The publish is refused rather than reported as done, because an app that
believes it has a database would write to nothing.
FixPublish to the hosted platform, or remove postgres: true.
"spryloom.yaml is missing, and this app needs a one-sentence description."
WhySpryloom can write the manifest, but not the sentence your coworkers read before opening the app.
Fix
spry publish . --description "Tracks expenses that need a second look."
"This folder has no package.json and no index.html, so Spryloom cannot tell what to run."
WhyYou are probably publishing the root of a repository holding several projects.
FixPublish from the app's own folder.
A different message, "declares no frontend and no backend, so there is nothing
to run", is about your manifest: runtime.frontend and runtime.backend can't
both be none.
A publish is slow or stopped showing progress
Good to knowThe work happens on Spryloom, not on your computer.
spry publish,spry delete,spry rollback,spry restoreandspry restartstart the work and then watch it. If the watching stops, the work carries on, andspry appsshows how it ended. The live version stays live until the new one is answering.
"This version of spry is too old."
WhyPublishing, deleting, rolling back, restoring and restarting changed
how they talk to Spryloom, and an older spry can't follow them. Nothing was
changed.
Fixnpm install -g spryloom@latest, or use npx spryloom@latest.
Another publish of the app is already running
WhySomeone started one first, perhaps you in another terminal, or your
agent. Only one publish, delete or rollback of an app runs at a time, so spry
shows that one instead of starting a second.
FixNothing is lost. Publish again once it finishes, if you have changed something since.
"Stopped watching …"
WhyYou pressed Ctrl-C. Only the watching stopped.
Fixspry apps shows the new version once it is live. Running
spry publish again from the same folder while it is still going watches it
again rather than starting another.
"Lost contact with Spryloom while watching …"
Whyspry couldn't reach Spryloom for a minute, usually because of your
connection.
FixCheck spry apps first. Publish again only if it shows the publish
didn't happen.
"Spryloom restarted and picked this up again at …"
WhySpryloom restarted part-way, and picked the work up again with the same version number.
FixNothing to do.
"Spryloom was interrupted …"
WhyIt was stopped part-way three times, so it gave up rather than keep trying. Nothing changed; the live version is still live.
FixPublish again. If it happens again, tell us.
"Spryloom hit an unexpected problem while …"
FixTry again. If it keeps happening, the message ends with an id such as
op_4Hk2…. Quote it when you ask for help, and we can see exactly what
happened.
"The uploaded folder is no longer here, so this publish could not carry on."
WhyUploaded folders are kept only while their publish runs, and a publish stuck for a day is abandoned. Nothing changed.
FixPublish again.
A delete says the app is not fully deleted
WhyA delete is done only after checking that the machine, the database, the secrets and the custom domains are all gone. One wasn't, and the message names it.
FixRun the delete again. Every step is "remove if still there", so it picks up where it stopped.
The app is not running
spry apps says STOPPED
WhyThe app crashed or was stopped. STOPPED is read from the platform,
not remembered, so it means what it says.
Fix
spry logs expense-notes what it printed before it stopped
spry restart expense-notes start it again
If the logs say nothing, look for a line marked [fly]: that is the platform,
and it is where an out-of-memory kill shows up. A common cause is a query that
loads a whole table at once.
WarningDon't publish again to recover from a crash. The code hasn't changed, and a rebuild can pick up a dependency that moved since. Restart starts the version that is already there.
A coworker can't get in
They see a sign-in page and never get an email
WhyThey aren't allowed in, so Spryloom doesn't send a link. The page says the same thing either way, so it can't be used to find out who works somewhere.
FixCheck access.visibility in spryloom.yaml:
| It says | Do this |
|---|---|
private |
Only you and admins can open it. Change it to invited or company, publish again, then spry invite them. |
invited |
spry invite them. |
company |
Check they are at that email domain. |
They see "You don't have access to this app."
WhyThey signed in, but aren't on the list. The page tells them who to ask.
Fixspry invite them, or change who the app is for.
"That invitation link has already been used or has expired."
WhyThe button in an invitation works once, for 7 days.
FixThe page it opens has their address filled in. They press Email me a link and sign in from that email. Once signed in, they stay signed in to that app for 30 days.
The invitation opened on their phone, and now their computer asks them to sign in
WhyThe invitation signed them in on the phone only.
FixOn the computer, they sign in with an emailed link as usual.
The sign-in link didn't work
WhySign-in links last 15 minutes and work once.
FixAsk them to request another.
Workspaces and sharing
How workspaces and sharing work is explained on workspaces and sharing.
I signed in with my work email and got a personal workspace
WhyYour domain's mail doesn't run on one of the business mail services that make a company workspace, such as Google Workspace or Microsoft 365.
FixShare apps by name: set visibility: invited and spry invite each
person. Everything else works the same.
A colleague can't see my app in their dashboard
WhyBeing in the same workspace shows nobody your apps until you share them.
FixSet visibility: invited and spry invite them, or set
visibility: company for everyone at your domain. Then publish.
"There is no app called …"
WhyThe name is misspelt, or the app isn't shared with you. Spryloom answers the same either way.
FixCheck the name with spry apps. If it is a colleague's app, ask them
to share it with you.
"The name … is already used in this workspace."
WhyA colleague has an app with that name that isn't shared with you.
FixChange app.slug in spryloom.yaml and publish again.
"This is a personal workspace, so there is no company for "company" to mean."
FixSet visibility: invited and name the people, or use
visibility: link.
Publishing with company says access.domain must be your domain
FixSet access.domain to your workspace's domain, which the message
names, or use visibility: invited.
Jobs, email and outside services
"This app declares scheduled jobs, and this deployment cannot run them."
WhyThe Spryloom you are publishing to has no scheduler.
FixRemove the jobs section, or publish to the hosted platform.
"This app sends email, and this deployment has no gateway to send it through."
WhyThe same, for email: true or egress.
FixPublish to the hosted platform, which has one.
"The app's scheduled jobs could not be set up."
WhyThe line under the message gives the reason. The app wasn't published, and the version before it is still serving, with its jobs.
FixPublish again. If it keeps happening, send that line to support.
A job's schedule is refused
WhyDuring the beta a job runs at most once every 10 hours, so a schedule
like "0 * * * *" is refused. A schedule that never comes round, such as 30
February, is refused too.
FixUse the schedule the refusal suggests.
A job is paused
WhyIt failed 5 times in a row, and you were emailed.
Fixspry logs <app> --job <job> shows what it printed. Fix it and
publish, or run spry jobs resume <app> <job>.
A host is refused when I publish
WhyEvery host in egress has to be on outside APIs.
The refusal names the host and says why: not on the list yet, never allowed, or
written as a URL instead of a host name.
FixWrite api.stripe.com, not https://api.stripe.com/v1. Slack's and
Discord's incoming webhooks are held back, because anyone can create one; post
to Slack through its bot API at slack.com instead.
A request to an outside service fails from the app
WhyThe gateway answers 403 for a host the app didn't declare, and names
it. A service that redirects to a different host needs that host declared too.
Anything but HTTPS on port 443 is refused, and so is an address instead of a
name.
FixAdd the host it names to egress and publish again.
A notification is refused
WhyThe answer says why. Most often: the person isn't invited and hasn't signed in to the app, the text contains a web address, or the app has already sent 5 today.
FixFor a link to a page in the app, use button.path instead of putting a
web address in the text.
Data and secrets
The app can't connect to its database
WhyDATABASE_URL is given to the app when it starts, so it needs
postgres: true in the manifest and a publish after adding it.
FixCheck the manifest, then publish again.
A secret isn't reaching the app
WhySecrets reach the app when it is published.
FixSet it, then publish again.
The app can't start without a secret, and the first publish failed
WhyAn app can't be given a secret before it exists.
FixPublish, set the secret, then publish again.
I've lost the only copy of a key
WhySpryloom can't give it back. No command, page or API returns a secret's value, on purpose.
FixGet another from wherever it came from, and set it again.
Undoing a change
A new version is wrong
Fix
spry rollback expense-notes back to the previous version
spry rollback expense-notes --to 3 back to a particular one
WarningRolling back changes which code runs, nothing else. If the problem was a database migration your app ran, rolling back doesn't undo it. Keep your own export of anything you can't afford to lose.
Someone has left, or a laptop is lost
Fixspry logout --all signs out every machine you are on, this one
included, by revoking every token you hold. Sign in again afterwards.
To remove someone else, take them out of access.admins or the invited list
and publish again.
What isn't built yet
Named here so you don't have to find out the hard way:
| Not yet | Instead |
|---|---|
| Google sign-in | Email links only. signin: [google] is accepted and does nothing. |
| File uploads | Refused when declared, so a manifest never describes something that doesn't exist. |
| Python | Node only, for now. |
| A backup command | Restores are done for you by us. |
| Changing a role without publishing | Edit access.admins and publish again. |
Scheduled jobs, email and outside services are built: see jobs and email and outside APIs.
Still stuck
Run spry check in the app's folder: it shows what Spryloom sees and whether it
could publish, without changing anything.
If a message didn't tell you what to do, that is a bug in the message, and worth telling us at hello@spryloom.com. The wording is part of the product.