Docs menu

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.

Tipspry check shows 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

  1. Look in spam or junk, and search for "Spryloom".
  2. Check the address you typed for a mistake.
  3. 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 restore and spry restart start the work and then watch it. If the watching stops, the work carries on, and spry apps shows 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.

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.

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.