# Lines hushrun prints

Every line hushrun prints as it runs, skips, checks a push or sets up a repository, grouped by when you see it, with what it means and what to do.

- `hushrun run`'s lines and the pre-push hook's go to stderr, and their first line starts with `hushrun: `. The tables leave that prefix out.
- `hushrun ready` prints its report on stdout, and `hushrun init` its plan.
- Errors start with `error: ` and warnings with `warning: `, and the tables keep those.
- Everything else is your command's own output.
- A line about sharing or recording never changes a check's exit code. hushrun runs the command whenever it can't skip, and exits as the command did, or 0 when it skips.
- `-q` silences hushrun's lines. `-v` adds more of its reasoning.
- In the examples, `test` is the check's name, `acme` your organization and `a0b1c2d` the commit.

## When a check runs or skips

The first and last line of each `hushrun run`.

| Line | What it means | What to do |
|---|---|---|
| `test running: no pass yet for this exact code` | No pass is recorded for [what hushrun compares](https://hushrun.com/docs/run.md#same-code), so the command runs | Nothing. Once it passes, the same code skips |
| `test passed in 41s; it won't run again on this code` | It passed, and the pass was recorded | Nothing |
| `test skipped: this exact code passed 3 min ago (saves 41s)` | A pass for this exact code was found. The command didn't run, and hushrun exited 0 | Nothing. To see its output, [run it again](https://hushrun.com/docs/daily.md#run-again) |
| `test skipped: this exact code passed in CI 3 min ago (saves 41s)` | The same, for a pass CI recorded | Nothing |
| `test running: it failed on this exact code last time` | The newest result for this code is a failure, so it runs again | Fix the code. If it passes now, it may be [flaky](https://hushrun.com/docs/daily.md#flaky) |
| `test failed in 2s (exit 1)` | It failed. The failure was recorded, and the command's exit code kept | Fix the code, not the check |
| `test running (--force)` | `--force` runs it even after a pass, and records the new result | Nothing |
| `test stopped by a signal; nothing recorded` | It was interrupted, for example with Ctrl-C | Run it again |
| `test passed in 12s, but files changed while it ran, so it wasn't recorded. If test writes files, list them in .gitignore` | The files changed during the run, so the result belongs to neither version | List what the check writes in `.gitignore`: see [Generated files](https://hushrun.com/docs/daily.md#generated-files) |
| `test running: can't read earlier results (…)` | The backend didn't answer within 10 seconds, or answered something unexpected. The check runs, and hushrun still tries to record | Nothing. If it keeps happening, see [Troubleshooting](https://hushrun.com/docs/troubleshooting.md) |
| `test running without skipping: can't tell which code this is (this policy has several scopes (api, web); pass --scope NAME or --all-scopes)` | `hushrun.yaml` has several scopes, and the command names none | Add `--scope NAME` to the check's command: see [Scopes](https://hushrun.com/docs/monorepos.md#scopes) |
| `test running without skipping: can't tell which code this is (no git repository at or above …)` | It runs outside a git repository, and records nothing | Run it inside the repository |
| `test running without skipping: results are off (package.json's hushrun.backend: … use https://)` | The backend's URL isn't `https://`, or `http://` on this machine | Fix the URL: see [Configuration](https://hushrun.com/docs/reference/configuration.md#package-json) |
| `can't start vitest: No such file or directory (os error 2)` | The command wasn't found, and hushrun exits 127. A command found but not runnable exits 126 | Run the script through your package manager, such as `npm test`, or give the command's path |

### In CI

| Line | What it means | What to do |
|---|---|---|
| `test running: this exact code passed in another CI job, and a CI pass counts in its own job` | The pass came from a job with another name, or on another system | Nothing. See [Jobs and variants](https://hushrun.com/docs/ci.md#variants) |
| `test running: job test of .github/workflows/ci.yml has several variants, which a run's key doesn't tell apart, so no pass skips it: to skip, name each, such as npx hushrun run test-${{ strategy.job-index }} --` | The job has a matrix, so one pass can't speak for every variant. The check runs every time | [Give each variant its own name](https://hushrun.com/docs/ci.md#variants) |
| `test running: this job has several instances, which a run's key doesn't tell apart, so no pass skips it` | On GitLab, the job uses `parallel` or a matrix | [Give each variant its own name](https://hushrun.com/docs/ci.md#variants) |
| `test running: …, so hushrun can't tell its variants, and no pass skips it` | hushrun can't read the job, as in a reusable workflow, so it runs every time | See [Jobs and variants](https://hushrun.com/docs/ci.md#variants) |
| `test passed in 41s` | In a job that no pass skips, it passed and was recorded | Nothing |
| `test skipped: this exact code passed in CI 3 min ago (saves 41s); skipped only on CI's passes` | The repository has **CI reuses only CI's results** turned on | Nothing. See [CI reuses only CI's results](https://hushrun.com/docs/ci.md#ci-only) |
| `test running: no CI pass yet for this exact code; skipped only on CI's passes` | The same setting, with no pass from CI on this code | Nothing |

### Where a check counts

These come from a `counts_from` rule in `hushrun.yaml`.

| Line | What it means | What to do |
|---|---|---|
| `test running: this run won't count: needs go 1.25, this is go1.24.3` | This machine doesn't meet the check's rule. hushrun says so before the command starts | Let it run. The line after it decides |
| `test passed in 6s; it won't count: needs go 1.25, this is go1.24.3` | It ran and kept its exit code, but recorded nothing. `hushrun ready` lists it as `ci` | Leave it to CI or a machine that meets the rule. Don't edit `counts_from` to get past it. See [Where a check counts](https://hushrun.com/docs/where-a-check-counts.md) |
| `test passed in 6s on linux, go1.25.14; it counts` | The machine met the rule, and the pass was recorded | Nothing |

### Variables

| Line | What it means | What to do |
|---|---|---|
| `HUSHRUN_NO_CI_OUTPUT is "maybe", not true or false, so it's ignored` | hushrun can't read the variable, and goes on as if it weren't set | Fix the value or remove the variable: see [Environment variables](https://hushrun.com/docs/reference/environment.md) |
| `HUSHRUN_INVOKED_BY is "ci", not agent:<id> or human:<id>, so runs aren't labeled` | The label isn't in the form hushrun takes | Set it to `agent:<id>` or `human:<id>` |

## Signing in and sharing results

These say why a result wasn't shared with your organization. The check still ran, and its exit code is kept.

| Line | What it means | What to do |
|---|---|---|
| `sign in to share results with acme: npx hushrun login` | Nobody signed in on this machine. hushrun says this at a terminal only | Run `npx hushrun login` once |
| `results aren't shared with acme: …` | No credential reached the backend: no sign-in, no `HUSHRUN_AGENT`, no `HUSHRUN_TOKEN`, and in GitHub Actions no `id-token: write`. The rest is the backend's reason | In a sandbox, set `HUSHRUN_AGENT`. In GitHub Actions, add `id-token: write`. In other CI, set `HUSHRUN_TOKEN` and `HUSHRUN_MACHINE_KEY`. See [Which credential goes where](https://hushrun.com/docs/rollout.md#credentials) |
| `results aren't shared with acme: no token: …`, with `HUSHRUN_AGENT` set | This hushrun is older than 0.6.0, and ignores `HUSHRUN_AGENT` | Move the repository to hushrun 0.6.0 or later: see [Agents in sandboxes](https://hushrun.com/docs/agents.md#sandboxes) |
| `test running: acme refused your login: …` | Your sign-in no longer works there, for example because you left the organization | Run `hushrun login` again |
| `test running: acme refused HUSHRUN_AGENT: agent token hra_AbC123 (agent:fleet-a) expired on 2026-10-20…` | The sandbox's token expired. `… was revoked on …` means someone revoked it | Create a token under the same agent name, and update the variable: see [Agents in sandboxes](https://hushrun.com/docs/agents.md#sandboxes) |
| `test running: acme refused HUSHRUN_TOKEN: …` | The agent token in `HUSHRUN_TOKEN` expired or was revoked | Create a new token, and update the secret |
| `test running: acme refused this job's ID token: …` | The backend doesn't accept this repository's CI | Check that the repository is connected to the organization, and wasn't deleted and created again under the same name: see [Set up CI](https://hushrun.com/docs/ci.md) |
| `HUSHRUN_TOKEN isn't sent to https://…: it goes only to the backend HUSHRUN_BACKEND names, and an agent token (hra_…) to hushrun's host, https://api.hushrun.com` | The repository names a backend this token may not go to | In a sandbox, use `HUSHRUN_AGENT`. Elsewhere, set `HUSHRUN_BACKEND` to the backend's URL: see [Which credential goes where](https://hushrun.com/docs/results.md#credentials) |
| `HUSHRUN_AGENT is damaged: part of it is missing, or something was added, such as a line break, so it's ignored` | The string was cut short or wrapped. hushrun shows none of it, since it holds a key | Copy it again, on one line |
| `HUSHRUN_AGENT comes from a newer hushrun than this one: update hushrun, so it's ignored` | A newer hushrun made the string | Update hushrun in the repository |
| `HUSHRUN_AGENT holds an agent token alone, not the whole string: …, so it's ignored` | It holds `hushrun token create`'s stdout, which is the token alone | Set the whole string, from a terminal or `--format json` |
| `HUSHRUN_TOKEN holds a HUSHRUN_AGENT string, which is never sent: set it as HUSHRUN_AGENT, so it's ignored` | The string is in the wrong variable | Set it as `HUSHRUN_AGENT`, and unset the variable the line names |
| `HUSHRUN_AGENT wins over HUSHRUN_TOKEN and HUSHRUN_MACHINE_KEY: it holds the backend, the token and the key together` | Other credentials are set beside the string, and hushrun ignores them | Remove the variables it names |
| `error: not signed in: …`, with a link to your machines page | You have 20 machines in that organization, the most it takes | Revoke one at `https://app.hushrun.com/account/machines`, then run `hushrun login` again |
| ``error: not signed in: … To make this machine a new key, remove …, then run `hushrun login` again`` | You or an admin revoked this machine, and a revoked machine doesn't come back | Remove the key file it names, then run `hushrun login`. Sign in again in your other organizations too |

## Results that aren't recorded

These end a run that passed or failed as usual, with its exit code kept. Nothing was recorded, so the next run on this code runs again.

| Line | What it means | What to do |
|---|---|---|
| `test passed in 41s; not recorded: …; upgrade: https://app.hushrun.com/acme/-/billing` | Your organization reached its plan's limit this period: on Free and Pro, the checks it includes; on Team and Growth, the spend cap. The `…` is the server's reason, below | An owner or admin upgrades at that page, or wait for the next period: see [At the limit](https://hushrun.com/docs/plans.md#at-the-limit) |
| `test failed in 2s (exit 1); not recorded: …; upgrade: https://app.hushrun.com/acme/-/billing` | The same, for a check that failed. Its exit code is kept | The same |
| `acme used the 2,000 recorded checks its Free plan includes this month, so checks run instead of skipping until 2026-11-01` | The server's reason on Free and Pro | The same |
| `acme reached its spend cap for this period (10,000 recorded checks, $50 of overage), so checks run instead of skipping until 2026-11-01` | The server's reason on Team and Growth, past the spend cap | Raise the cap on the Usage page, or wait for the next period: see [Overage and the spend cap](https://hushrun.com/docs/plans.md#spend-cap) |
| `warning: check results aren't recorded: …; upgrade: https://app.hushrun.com/acme/-/billing` | A pipeline command, `hushrun run --stage` or `hushrun attest --check`, says this once per command, with the server's reason. On GitHub Actions it's a warning annotation titled `hushrun: check results not recorded` instead | The same as above |
| `pass     unit (exit 0; not recorded)`, from `hushrun run --stage` | That check's result wasn't recorded for the plan. `run --stage` still exits 0 when its checks passed and 8 when one failed | The same |
| `not recorded check: unit`, from `hushrun attest` | The check event wasn't recorded for the plan. `attest` still exits 0 | The same |
| ``test passed in 41s; not recorded: this machine isn't registered: run `hushrun login` `` | A result from your sign-in needs this machine's signature. The machine has no key yet, was revoked, or went 90 days unused | Run `hushrun login` on this machine |
| ``test passed in 41s; not recorded: set `HUSHRUN_MACHINE_KEY` to the key `hushrun token create --agent` printed`` | An agent token is in `HUSHRUN_TOKEN`, without the key it came with | In a sandbox, use `HUSHRUN_AGENT`. In CI, set the key: see [Which credential goes where](https://hushrun.com/docs/rollout.md#credentials) |
| ``test passed in 41s; not recorded: HUSHRUN_AGENT's key can't sign for its token: make a new string with `hushrun token create --agent` `` | The key inside the string doesn't belong to its token. No variable replaces it | Make a new string with `hushrun token create --agent` |
| `test passed in 41s; not recorded: agent token hra_AbC123 records results for github.com/acme/shop only, and this result is for github.com/acme/web…` | The token belongs to another repository | Use that repository's token, or one made with `--org-wide` |
| `test passed in 41s; not recorded: agent token hra_AbC123 (agent:fleet-a) recorded 300 results in the last minute, its limit…` | The fleet records faster than its token allows | Slow it down, or create the token with a higher `--rate` |
| `test passed in 41s; not recorded` | The backend refused the credential. A line about sharing says why | See [Signing in and sharing results](#sharing) |
| `test passed in 41s, but it couldn't be recorded (…)` | The backend couldn't be reached when hushrun recorded | Nothing |
| `test passed in 12s, but it wasn't recorded: can't read the files again (…)` | hushrun couldn't read the files after the run, to check they hadn't changed | Run it again |
| ``the skip wasn't recorded: this machine isn't registered: run `hushrun login` `` | The skip worked, and the next run skips too. Only the time it saved wasn't counted | Run `hushrun login` on this machine |

## hushrun ready

`hushrun ready` prints a summary, one line per check, and the commands to run:

```console
$ npx hushrun ready
a0b1c2d isn't ready: CI would run 2 checks again.
  missing test   no pass yet for this exact code
  missing lint   it failed on this exact code last time
  passed  types
Run these here, then push:
  npm run test
  npm run lint
```

It exits 0 when no check is `missing`, and 10 otherwise. [hushrun ready](https://hushrun.com/docs/reference/cli.md#hushrun-ready) has its flags, `--strict` included.

### The summary

| Line | What it means | What to do |
|---|---|---|
| `a0b1c2d is ready to push.` | No check is `missing` | Push |
| `a0b1c2d isn't ready: CI would run 2 checks again.` | Some checks haven't passed on this code, and a command here can pass them | Run the commands under `Run these here, then push:`, each on its own |
| `a0b1c2d isn't ready: 1 check left, and nothing here changes that.` | With `--strict`: only `ci` checks are left | Leave them to CI, or run them on a machine that can |
| ``hushrun knows no checks for a0b1c2d: hushrun.yaml's stages, package.json's `hushrun run` scripts and CI's files name them.`` | Nothing in this repository goes through hushrun | Run `npx hushrun init` |
| `These run on the files as they are now, and those aren't a0b1c2d: commit or stash what's different first.` | The commands would run on your edits, not on the commit | Commit or stash, then run them |
| `CI runs bun test itself in .github/workflows/ci.yml (job test), so it won't skip there: run npx hushrun init to put it through hushrun.` | CI runs the command outside hushrun, so a pass here can't skip it | Do what the line says |
| `hushrun.yaml has a rule for go-tset, and nothing runs it` | A `counts_from` rule names a check that nothing runs, probably a typo | Fix the name in `hushrun.yaml` |

### Each check

| Line | What it means | What to do |
|---|---|---|
| `passed  test  recorded by …` | It passed on this code | Nothing |
| `missing test  no pass yet for this exact code` | Nothing is recorded for this code | Run the command `ready` prints |
| `missing test  it failed on this exact code last time` | Its newest result is a failure | Fix the code, then run it |
| `missing test  it failed on this exact code last time, and a run here wouldn't count (…): change the code, or pass it on a machine that meets the rule` | It failed, and this machine doesn't meet its `counts_from` rule | Change the code, or pass it on a machine that meets the rule: see [In hushrun ready](https://hushrun.com/docs/where-a-check-counts.md#ready) |
| `ci      test  it passed in another CI job, and a CI pass counts in its own job` | The pass came from another CI job, so CI runs it again. A run here would only skip, so it can't help | Nothing |
| `ci      test  this repository's CI reuses only CI's results, so a pass here wouldn't change what it runs` | **CI reuses only CI's results** is on | Nothing |
| `ci      test  a run here didn't count: needs go 1.25, this is go1.24.3` | A run here under its rule records nothing | Leave it to CI: see [In hushrun ready](https://hushrun.com/docs/where-a-check-counts.md#ready) |
| `ci      test  needs linux, this is macos` | This machine doesn't meet the rule's `os`, `arch` or `env` | Leave it to CI |
| `ci      test  CI runs it every time: job test of .github/workflows/ci.yml has several variants, which a run's key doesn't tell apart. To skip there too, name each, such as …` | No pass skips it in that job | [Give each variant its own name](https://hushrun.com/docs/ci.md#variants) |
| `ci      test  it passed here, but the result couldn't be recorded` | A run here wouldn't change what CI runs | Read the run's last line: see [Results that aren't recorded](#not-recorded) |
| `ci      test  go isn't installed here` | The command can't start here | Install it, or leave it to CI |
| `ci      test  results stay on this machine, so CI can't see a pass here` | You aren't signed in, and nothing names a backend | Run `npx hushrun login` |
| ``ci      test  not recorded: this machine isn't registered: run `hushrun login` `` | A pass here wouldn't be recorded | Run `hushrun login` |
| ``ci      test  acme didn't answer for this sign-in; `hushrun whoami` says why`` | The backend refused the credential | Run `hushrun whoami` |
| `ci      test  can't read earlier results (…)` | The backend couldn't be reached | Nothing |
| `ci      test  it names no --scope, and hushrun.yaml has several` | Its command can't tell which files it covers | Add `--scope NAME`: see [Scopes](https://hushrun.com/docs/monorepos.md#scopes) |
| `ci      unit  it needs NPM_TOKEN, which stays in CI` | A stage's check names a secret this machine doesn't have | Nothing |
| `ci      unit  it runs in its pinned image, and there is no Docker or Podman here` | A stage's check runs in a container | Install Docker or Podman, or leave it to CI |
| `ci      unit  staging takes CI's evidence only` | The stage is CI-only | Nothing |

A `ci` check doesn't count against the code, so it never refuses a push. `hushrun ready --strict` counts it.

## When a push is refused

The pre-push hook prints these. It runs no checks itself.

| Line | What it means | What to do |
|---|---|---|
| `push refused: CI would run test again on a0b1c2d` | A check it knows hasn't passed on the code being pushed: one a command here can pass, or one that failed and needs a code change. The lines after it say why, and what to run | Run the commands it prints, then push again: see [Before you push](https://hushrun.com/docs/daily.md#before-you-push) |
| `To push anyway, git push --no-verify: CI runs them.` | The hook was installed with `--refuse`, so it refuses people's pushes too | Run the commands, or push with `--no-verify` |
| `CI will run test on a0b1c2d. To skip that, run these before you push:` | A person's push: it goes on, and CI runs the check | Run the commands to save CI the run, or let CI run it |
| `a0b1c2d is ready: CI skips test, lint` | Every check that can run here passed | Nothing |
| `pushing without an answer about the checks: CI runs what has no pass` | No answer came within 15 seconds, so the push went on | Nothing |
| `installed the pre-push hook in .git/hooks/pre-push; hushrun hook uninstall takes it out` | `hushrun ready` or `hushrun run` found no hook in this clone, and installed it | Nothing. `hushrun hook uninstall` takes it out for good |

When the hook refuses what it shouldn't, or misses an agent, see [A push was refused](https://hushrun.com/docs/troubleshooting.md#push-refused).

## hushrun init

| Line | What it means | What to do |
|---|---|---|
| `error: hushrun init sets up a git repository: run it inside one` | It ran outside a git repository | Run it at the repository's root |
| `error: init can't ask without a terminal: run hushrun init --yes to apply` | A script or an agent ran it, with nobody to answer | Add `--yes` |
| `error: init can't ask without a terminal: add --yes to move the repository` | The same, for `--org` moving the repository | Add `--yes` |
| `error: github.com/acme/shop is for which of your organizations? init can't ask here: add --org <slug>, with one of …` | You belong to several organizations, and none is named like the repository's owner | Add `--org <slug>` with the one it's for |
| `Using acme, the organization named like the repository's owner: --org <slug> picks another of …` | init picked the organization named like the repository's owner | Nothing, or run it again with `--org <slug>` |
| `error: ana@acme.dev isn't a member of acme: yours are …` | `--org` names an organization you aren't in | Check the slug, or ask an admin of it to invite you |
| `error: signed in as ana@acme.dev, but the sign-in wasn't approved for an organization, …` | You approved the sign-in without picking an organization | Create or join one in the dashboard, then run init again |
| `Signed in as ana@acme.dev, but this repository records to acme, and ana@acme.dev isn't a member of acme.` | The repository already records to an organization you aren't in | Let init ask its admins to let you in. Once one approves, run init again |
| `ana@acme.dev is invited to acme: accept the invitation …, then run npx hushrun init again.` | You have an invitation you haven't accepted | Accept it in the dashboard, then run init again |
| `This repository already uses hushrun: its checks share results at https://api.hushrun.com/o/acme.` | Nothing to set up | Nothing. init still makes sure the organization has the repository |
| `HUSHRUN_AGENT is set, so init uses no login` | An agent runs here, so init plans as if nobody were signed in | Run init where a person can sign in |
| `warning: github.com/acme/shop isn't connected: …` | The backend refused to connect the repository, or couldn't be reached, so its GitHub Actions can't record yet | Read the reason, then run init again |
| `warning: the pre-push hook isn't installed: …` | init made its other changes | Run `hushrun hook install` |
| `test failed: once it passes, it skips on the same code.` | The check init ran to show a skip failed | Fix the code, then run it again |
| `Not running test: vitest isn't installed here.` | init didn't run that check to show a skip, since its tool is missing | Nothing. Install the tool when you want the check to run here |

init changes files only after you agree, or with `--yes`, and only the lines it listed. `git diff` shows them before you commit. [hushrun init](https://hushrun.com/docs/reference/cli.md#hushrun-init) lists everything it changes.
