Most refusals on a box are deliberate and say why. This page maps the message to the cause and to what to do.
Signing in
| Symptom | Cause · what to do |
|---|---|
| This box has no free seats. Ask your administrator to free one or raise the licensed seat count. | Every licensed seat is held by someone who signed in within the last 30 days. People already holding a seat still get in. An administrator disables someone on Administration → Users to free a seat, or you ask unotest to raise the count. The refusal is in the trail as seat.denied. |
| This account has been disabled on this box. | An administrator disabled the account here. Administration → Users → enable. |
| Those credentials were not accepted. | Break-glass username or password wrong, or the identity provider rejected the login (login.refused in the trail carries the provider’s reason). |
| That login attempt expired or was already used. Start again. | The browser came back to a callback whose attempt has been used or timed out (a replayed link, a long pause at the provider). Start from https://<box>/. |
redirect_uri_mismatch at the provider | The URI registered at your provider is not exactly https://<box>/_guard/login/callback. |
Sign-in works, everyone is readonly | No group mapping is in effect. For Google Workspace, part 2 (directory service account) is unfinished, or the mapping is empty. |
| none of this account’s groups are mapped to a role | The person is in no mapped group — or in a parent group; nesting is not followed. Add the group they are directly in. |
| Everyone is refused right after setup | Usually the impersonated administrator cannot read the directory, or a delegation scope is missing. The box refuses logins it cannot confirm. |
| Access outlives someone leaving | Groups are read from the sign-in token rather than the directory, so nothing re-checks membership. Only a directory source can offboard. |
429 too many login attempts | 10 attempts per minute per client address by default; wait for Retry-After. Behind your own proxy, every visitor may share one bucket — tell unotest the proxy’s address. |
Identity provider unavailable
- Open sessions keep working. A re-check that cannot reach the directory leaves the session alone and retries later; it never revokes on silence.
- New logins are refused while the directory cannot answer — admitting a person nobody could vouch for is the one case where waiting is wrong.
- Administrators use the break-glass account at
https://<box>/_guard/login/static. It exists for exactly this day. - The box boots even if the provider is unreachable at start-up; discovery is retried, never cached as failed.
Pushing a bundle
| Message | Meaning · what to do |
|---|---|
this box runs @unotest/web X or newer; the bundle pins Y (min-version) | The suite pins an older @unotest/web than the box supports. Bump it in unotest/package.json, commit the lockfile, push again. |
this token is not valid for this box (unauthorized) — the CLI adds the token is unknown, revoked, or for another box | Get a new one from unotest (tokens are issued per project by the box’s operator). |
… belongs to project "a" on this box, but the token is for "b" (scope-mismatch) | The repository is mapped to another project than the token’s. Use that project’s token. |
bundles on this box may not exceed N MB (too-large) | Something got into unotest/ that should not travel (recordings, databases, downloads). Ignore it in git. |
the upload did not arrive intact (malformed / id-mismatch) | Retry. If it repeats, a proxy between you and the box is rewriting the body. |
this token pushes bundles only (forbidden, on env push / env set) | The token has the bundles scope. Values need a token minted with the values scope. |
not pushed — N problem(s) above would fail on the box (exit 2) | Refused on your machine; each line names the file and the fix. Nothing left it. |
… answered 200 with something that is not a box's answer | The URL is not a box’s API — a login page, a proxy error. Check UNOTEST_BOX_URL; the box may also be older than this CLI. |
--run needs --env | Name the box environment to run in. |
no environment by that name on this box (unknown-environment) | Environments are the operator’s decision; ask which exist. |
the box no longer holds that bundle (unknown-bundle) | It was collected under the retention policy. Push again. |
name what to run with --collection <name>, or add a schedule (no-collections) | Nothing was named and the environment’s schedules name nothing. |
note: <env> has no value for NAME | The suite declares a name that environment neither sets nor holds. Runs there stop at the first step that reads it — set it with env push <env> or env set <env> NAME. |
An environment does not get its values
- The viewer’s Variables panel shows nothing under
box. The environment has no values yet: push them withnpx @unotest/web env push <env>(a token with the values scope), or set them on Administration → Environments → the environment. - A value set on the box is not what a run sees. A secret wins over a
variable of the same name, and a box-set value wins over the
configuration.
UNOTEST_*names are skipped byenv pushon purpose; set one withenv setif you mean it. - A push removed something.
env pushreplaces the layers: a name your files no longer carry is removed and listed. Put it back in the file, or useenv set. malformedfromenv push. The box has no values endpoint yet — it is older than the CLI. Ask unotest to update it.- The viewer reloaded and a debug session ended. Changing an environment’s values restarts its viewer, by design.
Environments page is empty
No environment is being served yet. The box has no bundle for that project. Push one; the viewer starts once there is something to show.
Schedules
- A schedule did not run after a switch in the viewer. Schedules run
the newest bundle of the environment’s
defaultBranch, not the bundle someone selected. Push to the branch. - A missed night was not caught up. By design: the next run is the next one due.
- A drift warning in the viewer. The schedule set the box computed
differs from the one pushed — usually a config that reads
process.env. Make the config compute the same set everywhere, or accept the box’s. - Cannot edit a schedule in the viewer. On a box schedules are
read-only. Edit
unotest/schedules.yamlorunotest.config, commit, push. - Nothing runs in a
pr-<n>environment on its own. Preview environments never arm schedules; CI orders their runs.
Licence
| Symptom | Meaning |
|---|---|
| Banner License expired — N day(s) of grace left on the Environments page | The licence has expired; the box keeps working for the grace period (14 days by default). Contact unotest to renew. |
| Page License required — The license has expired. | Grace is over. The box answers every request outside sign-in with this page until a renewed licence is installed. |
| No license file found. / The license file is invalid. | The licence file is missing or its signature does not verify. Contact unotest. |
GET /_guard/health → {"ok":true,"license":"grace"} | The same states, for your monitoring: valid, grace, expired, missing, invalid. |
What a box cannot reach
A run on the box executes inside the box’s container, on the box’s network. A scenario that passes on a laptop and fails on the box in its first step usually leans on something that exists only on the laptop:
localhostand port-forwards.kubectl port-forward, an SSH tunnel, a dev server on127.0.0.1— none of them exist on the box. Point preconditions andAPP_BASE_URLat the environment’s public URL, and keep the health probe on that URL too.- Tools on the host.
shell()runs inside the container: nokubectl, nogcloud, no project scripts outside the pushed suite. A scenario may call only files underunotest/— the bundle is the whole file system it gets (the push refuses a scenario that imports from outside). - Secrets in
shell()..secretsvalues are not in a child process’s environment by default, on a box or locally; the scenario names the ones it exports through thesandbox.exportSecretsallowlist. - A judge service.
UNOTEST_JUDGE_MODE=remotewith a service on your machine does not work from the box; uselocalmode with@unotest/judgein the suite — see Judge on a box. - Your repository. The box never reads it: it runs the pushed bundle and nothing else — see Pushing suites.
GitHub checks
- The check failed with “nothing was pushed for this commit”. The box
waited 15 minutes for a bundle with that commit’s sha and none arrived.
Make sure your CI job runs
bundle pushfor every commit the App reports on. - A pull request from a fork is not run. By design — it would receive the environment’s secrets. A maintainer re-requests the check in GitHub to opt in.
- A deployment did nothing. Its environment name is not mapped to a box environment; the box logs the missing name and drops the event.