---
title: Troubleshooting
description: Organized by what you're seeing, not by which subsystem is at fault — find your symptom and the fix.
order: 40
---

# Troubleshooting

Find the symptom. Each one names a concrete cause and fix.

## My run is stuck on `PENDING` forever

**Nothing is draining the queue.**

Triggering a run enqueues a Redis job. If no runner is consuming that queue, the run sits at `PENDING` indefinitely and nothing surfaces an error — the job was accepted, it's just never picked up.

The API detects this: a runner-presence watchdog logs a `WARN` once the queue stays non-empty for more than 60 seconds. **Check the API logs first.**

- **Self-hosted:** the runner is a separate service. API + web is not a complete stack. See [self-hosting](/docs/install/self-hosting).
- **Local development:** `pnpm dev:api` and `pnpm dev:web` don't start it. Use `pnpm dev` (which runs everything) or add `pnpm dev:runner` in a second terminal.

## I get a 401 with a token I know is valid

**Check the `izri_` prefix.**

The API short-circuits to bearer validation only when it sees `izri_`. Anything else falls through to cookie-session handling and returns an opaque `401` with no explanation.

A truncated copy-paste that lost the prefix looks exactly like an expired token. Verify the value before assuming the token is bad.

## I get a 404 for a project ID I'm certain exists

**The token probably belongs to a different organization.**

Cross-organization access returns `404`, not `403` — deliberately, so the API doesn't confirm a resource exists to someone who can't reach it.

Run `list_projects` (MCP) or check the dashboard to see what your token can actually reach.

## The Action can't find my project

Resolution order is `--project` / `project-id` → `IZRI_PROJECT_ID` → auto-match on the git `origin` remote.

Auto-match fails when:

- there is no `origin` remote, or it isn't GitHub
- the repository isn't bound to a project yet
- the token is in a different organization (see above)

Pass `project-id` explicitly to rule it out.

## The scope check fails immediately in CI

**`fetch-depth: 0` is missing.**

```yaml
- uses: actions/checkout@v4
  with:
    fetch-depth: 0
```

The scope check diffs against `origin/<base>`. `actions/checkout` defaults to a shallow clone with no base ref, so the diff can't be resolved. This is the single most common CI setup failure.

## `izri check-scope` produces a strange diff locally

**The default base is `origin/main`.**

If your trunk is `master`, `develop`, or anything else, pass it:

```bash
izri check-scope --base origin/develop
```

Wrong base gives you a *wrong* diff rather than an empty one, which is why the result looks strange rather than obviously broken.

## My `.izri/scope.yml` seems ignored

Two possibilities.

**It's malformed.** A file that doesn't parse falls back to the dashboard config or defaults and emits an `info`-level finding. `info` doesn't block, so it's easy to miss. The delta report records which source was used — `in_repo`, `dashboard`, or `defaults`. If it says anything but `in_repo`, your file didn't parse.

**It isn't at the PR head.** Resolution reads the file from the head commit. Adding it to `main` after opening the PR won't apply until you merge or rebase.

## A sensitive path was touched and nothing failed

**Working as designed.** `sensitive_path` is emitted at `info` — a "look here" pointer, not a gate.

If you want a genuine block, express it as `forbids` on a category in [`.izri/scope.yml`](/docs/configure/scope-yml). That emits `pr_type_forbidden_category` at `error`, which is a hard rule.

## Tests pass but `izri/hallucination` fails

**Working as designed, and this is the signal's whole purpose.**

Your tests passed without executing the lines you changed. `change_uncovered` is a hard rule, so it fails the umbrella regardless of a green suite.

The per-file detail lists exactly which lines went unexercised. Write a test that reaches them. See [`izri/hallucination`](/docs/signals/hallucination) for a worked example.

## Login redirects in a loop with no error

**The OAuth callback URL doesn't match the configured app URL.**

A port mismatch between where your browser reaches the app and where Izri thinks it lives produces a silent redirect loop rather than an error message. Make them match exactly.

## `izri/visual` reports `no_baseline` on everything

**Expected on the first run.** There is nothing to compare against yet. That run establishes the baseline; the next one can detect regressions.

## Visual diffs report tiny changes I didn't make

Differences under **0.1%** are treated as `matched` precisely because anti-aliasing and sub-pixel font rendering produce 0.01–0.05% noise between identical runs. If you're seeing reported diffs above that, something did change — check `dimensions_changed`, which means the viewport or element size shifted and made the comparison ill-defined.

## The umbrella is `unknown`

Not enough signal: the delta is still in flight, or every child skipped. **Treat it as "wait", not "go".** Branch protection blocks on it without marking it failed, which is correct.

## A run errored instead of failing

`ERROR` means the run itself broke rather than tests failing. It is treated as an analyzer error — a hard rule — because no evidence must never be read as "safe".

Check the run logs. Common causes are a missing dependency in the test environment or a timeout.

## Still stuck

Every page here is available as raw markdown — append `.md` to any URL — and the whole corpus is at `/llms-full.txt` if you want to hand it to an agent.
