# Troubleshooting the deadline app
## At a glance
- **Goal:** match each failure you will actually meet to its cause and
its fix, without guessing.
- **You need:** any chapter — this page is the escape hatch every
other chapter points to. Read the failing line first, then the
symptom row, then run the chapter's check again.
- **Run:** the two diagnostic commands most rows lead to — is the
question executable, and what does the draft still miss:
```bash
node --import tsx src/cli.ts check-contract
node --import tsx src/cli.ts explain --candidate 2026-03-20 --event 2026-03-06
```
- **Files:** most fixes land in the adapter or the queries:
```text
src/to-case.ts MODEL_SPEC, fact predicates and units
src/query.ts question names and argument shapes
```
## Diagnose in order
Read failures outside in: first the thrown error (the application
failed before any answer existed), then `evaluationStatus` (the engine
declined), then `truthStatus` and values (the engine answered, and the
answer may be inconclusive). Each layer has its own fix, and skipping
a layer mislabels the fault — most often by reading a declined
evaluation as a wrong value.
## Symptoms
| Symptom | Cause | Fix |
|---|---|---|
| `PackageNotFound` on open | canon package not installed, or the spec names a version that is not there | `npm ci`; confirm `MODEL_SPEC` is `de.bgb.fristen@0.1.0` and the installed canon package is 0.1.5 |
| `FactError` with `nearest: [...]` | a predicate name the canon does not know — misspelt, or renamed by a model change | pick the intended name from `nearest`; fix it in `to-case` (facts) or `query` (questions) |
| `NEITHER` on every question | missing facts, an underived-but-declared claim, or a wrong candidate (an undeclared predicate raises `FactError` instead — see the row above) | first `check-contract` confirms the questions are executable, then `explain` on the draft: the ask list names the missing fields |
| collect answers `COMPUTED` with no values | the rules derived nothing for these facts — usually a missing premise | same as above: `explain` on the matching draft names the gap; do not render "no value" as an end date |
| `sources: []` on every answer | this canon build attaches no source anchors to answers | expected on this build: show the applied rule identifier as the address and state the empty list plainly |
| hash mismatch replaying a good capture | bytes compared across SDKs, a case or query edit, or a spec mismatch | replay with the same SDK that saved the capture; read the replay detail — it distinguishes mismatch from refusal from an integrity failure |
| `replay refused: capture model … ≠ replay model …` | the capture was saved under another spec | replay against the recorded spec, not the open one; versions are never mixed |
| `address already in use` on `npm start` | the port is busy, usually a previous server still running | stop the previous server, or `PORT=8788 npm start`; `HOST` overrides the loopback bind |
| `tsx: command not found` | the TypeScript runner is not installed | `npm ci` installs the pinned toolchain; the suite needs it too |
| type error on a predicate argument | a value that does not match the declared parameter type — a date where the canon wants a quantity, or a misspelt unit | read the parameter list from `questions('en')` and fix the adapter; `'14 calendar_day'` needs the exact unit name |
| `not-computed` on a previously fine case | the call now declines: a reading must be chosen, or a question needs a decision the engine will not make | the view shows the status (`Not computed: …`); treat it as a prompt for input, not as a crash |
| checksum mismatch on the download | a corrupt or substituted archive | re-download; do not proceed past a failed `shasum -c` |
## Two symptoms newcomers misread
Two rows deserve emphasis because newcomers misread them most. `NEITHER`
everywhere feels like the engine is broken; it means neither the claim
nor its negation is supported. Missing facts are one possible cause,
not the only one — full input also answers `NEITHER` on a wrong
candidate date. Check the query against the model first (an unknown
relation is an integration error, not a `NEITHER`), then investigate
the blockers: run `explain` before changing anything — on the partial
example case it asks for `durationDays` and reports the computed
intermediate as not askable.
The hash mismatch row feels like corruption; across SDKs it is normal.
TypeScript and Python return the same statuses and the same values on
the same case, but their canonical bytes differ, so their `result`
hashes differ too. Same-SDK comparison is the rule; the capture format
records enough (`adapter`, `model`, `versions`) to enforce it.
## Limits and errors
- This table covers the application's failures, not the canon's
content: if the rules compute a date you believe the provisions do
not support, that is a question for the canon author, with the rule
identifier and the capture attached.
- Python has no `questions`, no `unfold`, and no `focused_truth`. A
Python `AttributeError` on one of those names is not a bug in your
code — read the manifest from the TypeScript SDK instead.
- A document-checksum failure on a capture has no fix except
discarding the file. Re-export from the case that produced it.
## Check your understanding
Break one thing on purpose, watch the expected failure, then restore
it.
**Stage 1 — break the adapter.** In `src/to-case.ts`, delete the
`frist_dauer_tage` line from `toFacts` (leave `toDraftFacts` as it
is), then run the missing-facts tests:
```bash
node --import tsx --test test/missing.test.ts
```
Exactly one test fails — the complete form now sends no duration, so
the questionnaire asks for `durationDays` again instead of stopping on
`NEITHER`:
```text
not ok 5 - a complete case with a wrong candidate ends the questionnaire on NEITHER
expected: 'none-askable'
actual: 'ask'
# tests 14
# pass 13
# fail 1
```
**Stage 2 — restore the line** and run the same command again. The
suite is green:
```text
ok 1 - a draft with only the event asks for durationDays
ok 2 - an empty draft asks for both fields
ok 5 - a complete case with a wrong candidate ends the questionnaire on NEITHER
# tests 14
# pass 14
# fail 0
```
If stage 1 shows no failure, the tests are not running against your
edit — check the working tree before trusting any green line.
Predict before you open each answer.
In stage 1, why do the draft tests 1–4 still pass?
They build their facts with `toDraftFacts`, which still sends the
duration when the draft has one. Only test 5 builds a complete case
with `toFacts`, the function you edited.
Every question answers `NEITHER` on a fully filled form. What do you run first?
`check-contract`, to confirm the questions are executable — an
unknown predicate would raise `FactError`, not `NEITHER`. Then
`explain` on the draft. A full form with a wrong candidate date also
answers `NEITHER`, so missing facts are only one possible cause.
## Next
- [Build with Arxo: route map](/build/)
- [Quickstart](/guide/quickstart/) to revisit the single-file version
- [How to read an answer](/guide/reading-an-answer/)
- [Connect an AI assistant](/guide/mcp/)