Grain / Docs / Troubleshooting

Docs

Troubleshooting

Start Grain troubleshooting with the doctor output, then match the failure to authentication, workspace selection, file synchronization, runtime type or desktop handoff. The recovery commands below preserve local work unless a force or discard option explicitly says otherwise.

Updated 2026-09-04By the Grain team

Collect the current state

grain doctor [path]
grain status

The doctor command accepts one optional path and no flags. It prints cli, auth, api, session, token expiry, link and remoteRoot lines. Status has no flags and reports login, resolved workspace, local root and sandbox state. Doctor rejects flags with Unknown doctor option: <option> and multiple paths with Usage: grain doctor [path]. Status rejects arguments with Unknown status option: <option>.

Authentication is missing

grain login

Login has no flags. It hands authentication to the Mac app and prints the path where the revocable CLI token was saved. If any option is supplied, it reports Unknown login option: <option>. Start the Mac app, complete sign-in and rerun the original command.

Remote drift blocks a push

grain pull [--force]
grain push [--force] [--no-reload] [--dry-run]
  1. When push says The remote workspace changed since this folder was last synchronized., do not force immediately. Pull first, review the resulting local files and preserve the edits you want.
  2. Pull has one flag, --force. Normal output reports remote changes pulled, local paths deleted and local changes preserved. When another client holds the workspace lease, pull identifies the active sync client.
  3. Push accepts --force, --no-reload and --dry-run. A successful retry prints pushed <n> path(s). Use --dry-run to see proposed paths; use --no-reload when an open workspace should not refresh.
  4. Use push --force only when you intentionally overwrite remote drift. The failure action explicitly recommends pull, review and retry first.

A static workspace has no compute runtime

grain upgrade [--target react-vite|nextjs]

Upgrade accepts only --target, with react-vite or nextjs; React with Vite is the default. It promotes a clean, pushed HTML workspace to an app runtime and does not update the CLI. Invalid targets return Usage: grain upgrade [--target react-vite|nextjs]. Local edits return This checkout has local changes that are not in the active managed-content revision. Push them first. Remote drift requires a pull and review. A workspace that already has a runtime reports This workspace already has an app runtime.

The desktop app does not open or refresh

grain open
grain refresh [path]

Open has no flags and reports that the desktop app was asked to open the selected workspace id. Refresh accepts one optional path and no flags, then reports that the app was asked to refresh previews for that workspace. When the desktop handoff is unavailable, both direct you to start the app and retry. Extra arguments return Usage: grain open or Usage: grain refresh [path].

Continuous sync does not start

grain start [path] [--foreground] [--restart] [--no-pull] [--continue|--abort] [--no-open] [--debounce-ms <n>] [--push-interval-ms <n>]
grain stop [path]

Start accepts --foreground, --restart, --no-pull, --continue, --abort, --no-open, --debounce-ms and --push-interval-ms. Healthy output reports the path watched by continuous sync. Use --restart when a stale daemon must be replaced. --continue and --abort are mutually exclusive; negative debounce and nonpositive interval values are rejected. Checkout-mode folders report Continuous sync cannot run from a checkout folder. Use explicit push and pull there. Stop accepts one optional path and no flags; checkout mode reports A checkout folder does not run the continuous sync daemon.

A checkout cannot be released

grain release [--dry-run]
grain checkouts list
grain checkouts remove <workspace-id> [--discard] [--dry-run]

Release accepts --dry-run and removes only a clean, validated checkout. Expected output is Removed checkout <id> at <path> or its Would remove preview. The checkouts list subcommand has no flags. Checkouts remove accepts --discard and --dry-run. If it reports Checkout <id> has unpushed changes., push the checkout or rerun with --discard only when those changes are intentionally abandoned. Repeating a dry-run flag reports Use --dry-run only once.

An option or value is rejected

  • Read the usage text in the error. Command flags belong after that command; global --json and --workspace <id> belong before it.
  • When the message says Missing value for <flag>, provide a value that does not begin with another option marker.
  • Do not combine mutually exclusive choices such as start continue and abort, or public and password sharing.
  • Use each command's dry-run option at most once. Dry runs report proposed state without writing it.

Frequently asked questions

Why does Grain say the remote workspace changed?

Another client changed the remote state after your folder last synchronized. Pull, review the local result and push again.

Why can I not run a command in a static workspace?

Static HTML workspaces have no compute runtime. Push a clean checkout and promote it only when the artifact needs an app runtime.

Why does the Grain app not open from the CLI?

The desktop handoff is unavailable. Start the Mac app and retry open or refresh.

Can I delete a checkout with unpushed changes?

The safe commands refuse by default. Push wanted work first; use discard only when you deliberately want to abandon the local changes.

Return to a known state

Run the diagnostic commands, preserve local edits and apply the recovery for the exact error code or message.

Free forever. No card. Bring your own agent.