> ## Documentation Index
> Fetch the complete documentation index at: https://docs.decktalk.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting

> Find a DeckTalk message, verdict, or warning by the text it prints, with its cause and fix.

This page lists each message, verdict, and warning that DeckTalk prints, with its cause and its fix.
Search the page for the text you see.

A line that starts `error[CODE]:` is an error, and the command exits 3 without finishing its work. A
verdict or a note is a finding, and the command finishes and exits 1, or 0 when the finding is
uncertain and `--strict` was not passed. [Exit codes](/reference/cli#exit-codes) has the whole set.

If the project has no voiced build yet, run `decktalk build --no-voice` first. It spends no credits,
and it shows if the problem is in the cues, the pages, or the assembly. If the project has a voiced
build, rehearse in a copy of the project. A build without voice stops on voiced takes, because it would empty the narration cache, as
[A build without voice and a voiced build](/guides/rebuild-one-section#a-build-without-voice-and-a-voiced-build)
explains.

## Find your message

| Text you see                                            | Printed by                               | Entry                                                                     |
| ------------------------------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------- |
| `playwright install failed`                             | `install`                                | [Entry](#playwright-install-failed-in-install)                            |
| `MISSING`                                               | `doctor`                                 | [Entry](#missing-in-doctor)                                               |
| `is not empty`                                          | `init`                                   | [Entry](#is-not-empty-in-init)                                            |
| `must be letters`                                       | `init`                                   | [Entry](#must-be-letters-in-init)                                         |
| `not set`                                               | `narrate`, `build`, `soundscape`         | [Entry](#not-set-in-narrate)                                              |
| `HTTP 401`, `HTTP 402`                                  | `narrate`, `build`, `soundscape`         | [Entry](#http-401-or-http-402-in-narrate)                                 |
| `could not reach`                                       | `narrate`, `build`, `soundscape`         | [Entry](#could-not-reach-in-narrate)                                      |
| `decktalk.toml is not there`                            | every project command                    | [Entry](#decktalk-toml-is-not-there-in-any-command)                       |
| `unknown table(s)`                                      | every project command                    | [Entry](#unknown-table-s-in-decktalk-toml)                                |
| `ignoring unknown key`                                  | every project command                    | [Entry](#ignoring-unknown-key-in-decktalk-toml)                           |
| `must be int, got float`, `UNREADABLE`                  | every project command                    | [Entry](#must-be-in-decktalk-toml)                                        |
| `invalid tuning value`                                  | every project command                    | [Entry](#invalid-tuning-value-in-decktalk-toml)                           |
| `unfilled placeholders`                                 | `narrate`, `build`                       | [Entry](#unfilled-placeholders-in-narrate)                                |
| `no spoken section matches`                             | `narrate`                                | [Entry](#no-spoken-section-matches-in-narrate)                            |
| `have no [[section]] in decktalk.toml`                  | `narrate`, `build`                       | [Entry](#have-no-section-in-decktalk-toml-in-narrate)                     |
| `have no take yet`                                      | `narrate`                                | [Entry](#have-no-take-yet-in-narrate)                                     |
| `holds paid takes`                                      | `narrate --no-voice`, `build --no-voice` | [Entry](#holds-paid-takes-in-narrate)                                     |
| `phrase not found`                                      | `align`, `build`                         | [Entry](#phrase-not-found-in-align)                                       |
| `could not be matched to the narration`                 | `build`                                  | [Entry](#phrase-not-found-in-align)                                       |
| `not in deck/index.html`                                | `align`, `build`                         | [Entry](#not-in-its-page-in-align)                                        |
| `appear nowhere in the page that plays them`            | `build`                                  | [Entry](#not-in-its-page-in-align)                                        |
| `UNRESOLVED`                                            | `align --json`, `verify`                 | [Entry](#unresolved-in-align-or-verify)                                   |
| `UNKNOWN CUE`                                           | `align --json`                           | [Entry](#unknown-cue-in-align)                                            |
| `shorter than the visuals need`                         | `align`                                  | [Entry](#shorter-than-the-visuals-need-in-align)                          |
| `is past the end of the audio`                          | `align`                                  | [Entry](#is-past-the-end-of-the-audio-in-align)                           |
| `occurs <N> times in this section`                      | `align`                                  | [Entry](#occurs-n-times-in-this-section-in-align)                         |
| `no words`                                              | `align`                                  | [Entry](#no-words-in-align)                                               |
| `is not in decktalk.toml`                               | `align`, `build`                         | [Entry](#is-not-in-decktalk-toml-in-align)                                |
| `NO CHANGE`, `THIN CHANGE?`                             | `preflight`                              | [Entry](#no-change-or-thin-change-in-preflight)                           |
| `PAGE ERROR`, `page error`                              | `record`, `build`                        | [Entry](#page-error-in-record)                                            |
| `STALLED`, `frame stall(s)`                             | `record`                                 | [Entry](#stalled-in-record)                                               |
| `TRUNCATED`                                             | `record`                                 | [Entry](#truncated-in-record)                                             |
| `NO COVER`                                              | `record`                                 | [Entry](#no-cover-in-record)                                              |
| `BLACK?`                                                | `record`                                 | [Entry](#black-in-record)                                                 |
| `KATEX ERROR`, `KATEX NOT LOADED`, `KaTeX did not load` | `record`, `verify`, `preflight`          | [Entry](#katex-error-or-katex-not-loaded-in-record)                       |
| `no narration span yet`                                 | `record`                                 | [Entry](#no-narration-span-yet-in-record)                                 |
| `is missing, so a slate plays`                          | `assemble`                               | [Entry](#is-missing-so-a-slate-plays-in-assemble)                         |
| `clip missing`, `recording missing`                     | `assemble` with `--strict`               | [Entry](#clip-missing-or-recording-missing-in-assemble)                   |
| `using black`                                           | `assemble`                               | [Entry](#using-black-in-assemble)                                         |
| `has no span in`                                        | `assemble`                               | [Entry](#has-no-span-in-in-assemble)                                      |
| `music missing`, `ambience missing`                     | `assemble`                               | [Entry](#music-missing-in-assemble)                                       |
| `unresolved; skipped`                                   | `assemble`                               | [Entry](#unresolved-skipped-in-assemble)                                  |
| `[loud] skipped`                                        | `assemble`                               | [Entry](#loud-skipped-in-assemble)                                        |
| `true peak`, `integrated loudness`                      | `assemble`                               | [Entry](#true-peak-or-integrated-loudness-in-assemble)                    |
| `BLACK`                                                 | `verify`                                 | [Entry](#black-in-verify)                                                 |
| `SPEECH AT CUT`                                         | `verify`                                 | [Entry](#speech-at-cut-in-verify)                                         |
| `POP AT CUT`                                            | `verify`, `build`                        | [Entry](#pop-at-cut-in-verify)                                            |
| `NO CHANGE`                                             | `verify`                                 | [Entry](#no-change-in-verify)                                             |
| `THIN CHANGE?`                                          | `verify`                                 | [Entry](#thin-change-in-verify)                                           |
| `OFF CUE`                                               | `verify`                                 | [Entry](#off-cue-in-verify)                                               |
| `NO_CLICK`                                              | `verify --json`                          | [Entry](#no_click-in-verify)                                              |
| `skipped`                                               | `verify`                                 | [Entry](#skipped-in-verify)                                               |
| `malformed check`                                       | `verify`                                 | [Entry](#malformed-check-in-verify)                                       |
| `nothing to verify`                                     | `verify`                                 | [Entry](#nothing-to-verify-in-verify)                                     |
| `is not a section in decktalk.toml`                     | `assemble`, `verify`                     | [Entry](#is-not-a-section-in-decktalk-toml-in-assemble-or-verify)         |
| `The take index is not there`                           | `words`, `clip`                          | [Entry](#the-take-index-is-not-there-in-words-or-clip)                    |
| `has no section video`                                  | `clip`                                   | [Entry](#has-no-section-video-in-clip)                                    |
| `is a clip section`                                     | `clip`                                   | [Entry](#is-a-clip-section-in-clip)                                       |
| `cuts the word`                                         | `clip`                                   | [Entry](#cuts-the-word-in-clip)                                           |
| `data-text="spoken" text not found in the spoken words` | page                                     | [Entry](#data-text="spoken"-text-not-found-in-the-spoken-words-in-a-page) |
| `matches no element, handler, or slide`                 | page                                     | [Entry](#matches-no-element-handler-or-slide-in-a-page)                   |
| `unknown cue id`                                        | page                                     | [Entry](#unknown-cue-id-in-a-page)                                        |
| `owns no cue in ?cues=`                                 | page                                     | [Entry](#owns-no-cue-in-a-page)                                           |

## install, doctor, and keys

### `playwright install failed` in install

**Cause.** Playwright could not download Chromium or, on Linux, its system libraries. The output above
the error shows the reason.

**Fix.**

1. If the output names a network or proxy problem, set `PLAYWRIGHT_DOWNLOAD_HOST` as Playwright documents.
2. If you are on Linux, run `decktalk install` as a user who can use sudo.
3. Run `decktalk install` again.

**More.** [What install downloads](/requirements#what-install-downloads)

### `MISSING` in doctor

**Cause.** A component is not installed. The detail says which one.

* If `chromium` shows `MISSING`, Chromium does not launch.
* If `ffmpeg` shows `MISSING`, no ffmpeg exists in `DECKTALK_FFMPEG`, the pinned download, or `PATH`.
* If `katex` shows `MISSING`, the wheel's KaTeX copy is incomplete. The detail names each absent file.

**Fix.**

1. Run `decktalk install`, or reinstall DeckTalk when the `katex` row is the one that is `MISSING`.
2. Run `decktalk doctor` again.

`doctor` exits 1 on a `MISSING` row.

**More.** [`decktalk doctor`](/reference/cli#decktalk-doctor)

### `is not empty` in init

`init` prints `error[CONFIG]: <dir> is not empty (pass force to write into it anyway)` and exits 3.

**Cause.** The target directory already has files.

**Fix.**

1. If you want a new project, pass an empty or new directory.
2. If you want to write into this directory, add `--force`. `--force` overwrites every file the starter also writes.

**More.** [`decktalk init`](/reference/cli#decktalk-init)

### `must be letters` in init

`init` prints `error[CONFIG]: project name '<name>' must be letters, digits, dots, dashes or underscores` and exits 3.

**Cause.** The project name starts with a character that is not a letter or digit, or has another
character. The default name is the directory name.

**Fix.**

1. Pass a valid name with `--name`, such as `--name my-lesson`.

**More.** [`decktalk init`](/reference/cli#decktalk-init)

### `not set` in narrate

A voiced run prints `ELEVENLABS_API_KEY, ELEVENLABS_VOICE_ID not set.` or one of the two names.

**Cause.** The key or the voice id is missing. A value that starts with `<`, such as `<voice-id>`,
counts as unset.

**Fix.**

1. If the project has no `.env`, run `cp .env.example .env`.
2. Replace both placeholder values in `.env`.
3. Run the command again.

You can also export both variables. A build without voice needs neither.

**More.** [Accounts](/requirements#accounts)

### `HTTP 401` or `HTTP 402` in narrate

The error reads `HTTP <code> from https://api.elevenlabs.io/v1/text-to-speech/<voice id>/with-timestamps: <detail>`.

**Cause.**

* A `401` means that the key is wrong or was revoked.
* A `402` or a quota message means that the plan has no characters left, or that the voice needs a paid plan.

**Fix.**

1. If the code is 401, copy a new key into `.env`.
2. If the code is 402, check the plan's character balance with ElevenLabs.
3. Run `decktalk narrate --dry-run` to see the exact text that a build sends.

**More.** [What spends credits](/requirements#what-spends-credits)

### `could not reach` in narrate

**Cause.** The machine has no route to the speech provider. The reason follows the URL.

**Fix.**

1. Check the network connection and any proxy.
2. If you only need a rehearsal, run `decktalk build --no-voice` in a copy of the project.

**More.** [A build without voice and a voiced build](/guides/rebuild-one-section#a-build-without-voice-and-a-voiced-build)

## Project files

### `decktalk.toml is not there` in any command

```text theme={null}
error[CONFIG]: decktalk.toml is not there.
  hint: Run from a project directory, pass --project DIR, or create one with `decktalk init DIR`.
```

**Cause.** The command did not run in a project directory, and `--project` was not given.

**Fix.**

1. Run `cd` into the project directory.
2. If there is no project yet, run `decktalk init DIR`.

If you run from elsewhere, pass `-p DIR` instead. `decktalk status` is the one command that reports
this rather than raising it. It prints a `MISSING` row and exits 1, because reading the four input
files is its whole job.

**More.** [Global options](/reference/cli#global-options)

### `unknown table(s)` in decktalk.toml

```text theme={null}
error[CONFIG]: decktalk.toml: unknown table(s) ['mixx']. The known tables are ['audio', 'elevenlabs', 'mix', 'narration', 'output', 'project', 'record', 'section', 'soundscape', 'transition', 'verify', 'video', 'voice'].
```

**Cause.** A top-level table name has a typo, or it is not a DeckTalk table.

**Fix.**

1. Compare the name with the known list in the error.
2. Rename or remove the table.

An unknown key inside a table prints a warning instead. See
[`ignoring unknown key` in decktalk.toml](#ignoring-unknown-key-in-decktalk-toml).

**More.** [Load rules](/reference/decktalk-toml#load-rules)

### `ignoring unknown key` in decktalk.toml

```text theme={null}
decktalk.toml: [voice]: ignoring unknown key 'stabilty' (did you mean 'stability'?).
```

**Cause.** A key inside a table has a typo, or it belongs to another table or section kind. DeckTalk
ignores the key and continues, so the setting has no effect.

**Fix.**

1. If the warning suggests a key, rename the key to that name.
2. If the warning says the key applies only to a page or clip section, move it to that kind of section.
3. Run the command again and check that the warning is gone.

The per-machine settings file gets the same warning, with its own path first.

**More.** [Load rules](/reference/decktalk-toml#load-rules)

### `must be` in decktalk.toml

```text theme={null}
sections 0 | 1 certain, 0 uncertain finding(s)
UNREADABLE  decktalk.toml: decktalk.toml: [soundscape.music]: 'seconds' must be int, got float
```

**Cause.** A value has the wrong type. The error names the table, the key, and both types. `status`
reports it as an `UNREADABLE` row and exits 1. Every other command raises it and exits 3.

**Fix.**

1. Look up the key's type in [decktalk.toml](/reference/decktalk-toml).
2. Change the value to that type.

**More.** [Load rules](/reference/decktalk-toml#load-rules)

### `invalid tuning value` in decktalk.toml

**Cause.** A tuning field in `decktalk.toml`, the per-machine file, or a `DECKTALK_<TABLE>_<FIELD>`
variable has a value of the wrong type.

**Fix.**

1. Look up the field's type in [Configuration](/reference/configuration).
2. Correct the value where you set it.

**More.** [Configuration](/reference/configuration)

## narrate

### `unfilled placeholders` in narrate

The error reads `unfilled placeholders ['<NAME>'] in the script; fill them or pass allow_placeholders`.

**Cause.** The script still has a `[CAPS]` placeholder, and the run is voiced.

**Fix.**

1. Run `decktalk narrate --dry-run` to list every placeholder.
2. Replace each placeholder in `script.md`.
3. If you want to voice the text as it is, pass `--allow-placeholders`.

**More.** [Directions and placeholders](/reference/script-md#directions-and-placeholders)

### `no spoken section matches` in narrate

```text theme={null}
error[CONFIG]: no spoken section matches [5]. The spoken sections are [1, 2, 3].
```

**Cause.** `--only` names a clip section or a number with no script heading.

**Fix.**

1. Pass a number from the list in the error.

**More.** [`decktalk narrate`](/reference/cli#decktalk-narrate)

### `have no [[section]] in decktalk.toml` in narrate

**Cause.** The script has a `## N.` heading with no `[[section]]` table of the same number.

**Fix.**

1. If the section is new, add a `[[section]]` table with that `number`.
2. If the number is a typo, fix the heading in `script.md`.

**More.** [Headings](/reference/script-md#headings)

### `have no take yet` in narrate

**Cause.** `narrate --only` voiced some sections, and other spoken sections have no audio yet. The
narration then covers only the sections that have a take.

**Fix.**

1. Run `decktalk narrate` with no `--only`.

**More.** [Rebuild one section](/guides/rebuild-one-section)

### `holds paid takes` in narrate

```text theme={null}
error[CONFIG]: build/narration/takes.json holds paid takes for section(s) 01, and a run without voice would replace them in the take index, so the next voiced build would voice them again and spend credits on all of them.
  hint: Rehearse the sections nobody has paid for: --only 2 --only 3.
```

**Cause.** You ran `decktalk narrate --no-voice` or `decktalk build --no-voice` over a section that already holds a
paid take. The run would replace that row of the take index with a placeholder, and the next voiced build would pay
for the section again. The paid mp3 files themselves are never touched, because a placeholder take is named
`silent-<hash>.mp3` and a paid take is named by its own digest.

**Fix.**

1. Rehearse the sections the hint names, with `--only`.
2. If you want to rehearse the whole project, copy it and run the build without voice in the copy.
3. If you want to drop the paid takes, run the same command with `--force`.

The refusal covers only the sections the run was asked to write, so a film you have already paid for still rehearses
a new section for nothing.

**More.** [A build without voice and a voiced build](/guides/rebuild-one-section#a-build-without-voice-and-a-voiced-build)

### A section opens on its first word

**Cause.** The section's lead is 0, from its own `lead_seconds = 0` or from `[narration] lead_seconds = 0`.
`narrate` removes a direction before the first words of a section, `[pause N]` included, so a pause in the
script cannot open it either. The first word plays as the section's first frame appears.

**Fix.**

1. Set `lead_seconds` on the page section in `decktalk.toml`, or raise `[narration] lead_seconds` for every
   section. `narrate` places that much silence before the take and sends no request.
2. Run `decktalk build --only N`, so the recording covers the lead.

**More.** [Silence in a section](/reference/decktalk-toml#silence-in-a-section)

## align

### `phrase not found` in align

```text theme={null}
sec  speech   need  cues
 01    13.0      -  1.1title@0.5,1.1script@2.9,1.1deck@6.27,1.1word@11.56
 02    14.6      -  2.1average@1.44,2.1code@9.43,2.1waited@11.31
                    ! 2.1formula: phrase not found: 'the formular'
 03    11.9      -  3.1idea@1.41,3.1again@7.8,3.1make@8.71
3 sections with cues, 1 unresolved  (estimated words: times are placeholders)
error[CONFIG]: 1 cue(s) could not be matched to the narration, and a slide whose cues are unresolved never appears.
  hint: Fix these phrases in cues.json, or pass --allow-unresolved-cues:
  section 02: 2.1formula: phrase not found: 'the formular'
```

`decktalk align` on its own prints the same table, reports the row as an `UNRESOLVED` finding and exits 1.
`build` raises instead, so it exits 3 and records nothing.

**Cause.** The cue phrase is not in the section's spoken words. The usual reasons, most common first:

* The phrase has a typo.
* The script writes a number as words, and the phrase uses digits, or the other way around.
* The phrase uses another word form, such as "rates" for "rate".
* A hyphen joins two words into one token.
* The phrase has a curly apostrophe. Matching removes it, but the spoken word keeps a straight one.

**Fix.**

1. Open the section's `words_file` from `build/narration/takes.json`.
2. Find the words as the voice says them.
3. Copy those words into the cue's `on` value in `cues.json`.
4. Run `decktalk align`.

If you want to build without the cue, pass `build --allow-unresolved-cues`. A slide whose cues are all
unresolved never mounts.

**More.** [Matching rules](/concepts/cues#matching-rules)

### `not in` its page in align

```text theme={null}
 02    14.6      -  2.1average@1.44,2.1formula@5.67,2.1code@9.43,2.1waited@11.31
                    ! 2.1code: not in deck/index.html
                    ! 2.1codex: data-cue="2.1codex" in deck/index.html has no entry in cues.json
3 sections with cues, 0 unresolved  (estimated words: times are placeholders)
error[CONFIG]: 1 cue id(s) in cues.json appear nowhere in the page that plays them, so the page would never reveal them.
  hint: Add data-cue="2.1code" to the slide in deck/index.html, fix the id in cues.json, or pass --allow-unknown-cues. The ids are:
  section 02: 2.1code: not in deck/index.html
```

A rename usually prints both lines, because the id `cues.json` names is now missing from the page and the id
the page carries is missing from `cues.json`. The second line is the `UNCUED ELEMENT` finding.

**Cause.** `align` looks for each cue id in its page in quotes or backticks, and did not find this
one. Either the id in `cues.json` has a typo, or no slide uses the cue.

**Fix.**

1. If the id has a typo, fix it in `cues.json`.
2. If no slide uses the cue, add `data-cue="<id>"` to the element in the page.
3. If the page builds its cue ids at run time, pass `--allow-unknown-cues`.

A key of a slide's `preview` object in a `DeckTalk.scene` call also counts as a mention, because it is a quoted
literal. A `data-preview="2.1code@8"` attribute does not, because the id there is part of a longer string.

**More.** [Cue mismatches](/concepts/cues#cue-mismatches)

### `UNRESOLVED` in align or verify

**Cause.**

* In `align --json`, a note has the verdict `UNRESOLVED` when a phrase is not found or the section has no words.
* In `verify`, a cue named on the command line is not in `cue-times.json`. The row reads `UNRESOLVED: the cue is not in cue-times.json`.

**Fix.**

1. If `align` printed it, follow [`phrase not found` in align](#phrase-not-found-in-align).
2. If `verify` printed it, check the `SECTION:CUE` spelling against `build/cue-times.json`.
3. If the cue is new, run `decktalk align` before `verify`.

**More.** [`decktalk verify`](/reference/cli#decktalk-verify)

### `UNKNOWN CUE` in align

**Cause.** In `align --json`, a note has the verdict `UNKNOWN CUE` when a cue id is not in its page. The
table prints the same note as `<cue>: not in <page>`.

**Fix.**

1. Follow [`not in` its page in align](#not-in-its-page-in-align).

**More.** [`decktalk align`](/reference/cli#decktalk-align)

### `shorter than the visuals need` in align

The note reads `speech <S>s is <D>s shorter than the visuals need`.

**Cause.** The section's speech ends before the `min_seconds` that `cues.json` sets for it. This is an
uncertain finding, so `align` exits 1 only with `--strict`.

**Fix.**

1. If the slides need more time, add words or a `[pause N]` to the section in `script.md`.
2. If the slides are fine, lower or remove `min_seconds` in `cues.json`.

**More.** [cues.json](/reference/cues-json)

### `is past the end of the audio` in align

**Cause.** A cue time is later than the section's audio, usually because of a large offset key. This
note is not a finding.

**Fix.**

1. Reduce the cue's `offset` in `cues.json`, or move the cue to an earlier phrase.

**More.** [cues.json](/reference/cues-json)

### `occurs N times in this section` in align

The note reads `'<phrase>' occurs <N> times in this section, at <T>s, <T>s. The cue uses the first. Set "occurrence" to choose one.`

**Cause.** The cue phrase matches more than one run of the section's spoken words, and the cue does not set
`occurrence`. The cue fires on the first match. If you meant a later one, the slide appears early, maybe by
many seconds. This note is not a finding, so `align` exits 0, even with `--strict`.

**Fix.**

1. Compare the times in the note with the moment the visual should appear.
2. If a later match is right, set `"occurrence"` to its position, such as `2`, in `cues.json`.
3. If the first match is right, set `"occurrence": 1` to keep it and silence the note.
4. Or make the phrase longer, so it matches only once.

**More.** [Matching rules](/concepts/cues#matching-rules)

### `no words` in align

The note reads `no words (estimated takes)` or `no words (missing words file)`.

**Cause.** The section's words file is empty or missing, so no phrase can resolve. Only `$start`
resolves.

**Fix.**

1. Run `decktalk narrate` again. For a rehearsal, run `decktalk narrate --no-voice` in a copy of the project.
2. Run `decktalk align`.

**More.** [`decktalk narrate`](/reference/cli#decktalk-narrate)

### `is not in decktalk.toml` in align

The error reads `cues.json: section <N> is not in decktalk.toml`.

**Cause.** A section key in `cues.json` has no `[[section]]` table of the same number.

**Fix.**

1. If the key is a typo, fix it in `cues.json`.
2. If the section is new, add its `[[section]]` table.

**More.** [cues.json](/reference/cues-json)

## preflight

### `NO CHANGE` or `THIN CHANGE?` in preflight

**Cause.** The frozen frame at the cue differs from the frozen frame before it by less than the `[verify]`
floors, or by less than `thin_change_factor` times them. The reveal is small even in its end state, so `verify`
is likely to read the same on the final mp4.

**Fix.**

1. Open the row's two PNG files under `build/preflight/NN/`.
2. If the reveal is too small to see, make it larger or give it more contrast in the page.
3. If the reveal is small on purpose, mark the cue `"verify": false`. Or leave it, and check it by eye after the build.

**More.** [`decktalk preflight`](/reference/cli#decktalk-preflight), [`THIN CHANGE?` in verify](#thin-change-in-verify)

## record

### `PAGE ERROR` in record

`record` logs `[page] NN  page error: <text>`, where `NN` is the section number. `build` stops after
the record table. A page with no catalog logs
`no window.__decktalk.catalog (is decktalk-runtime.js included, and does the page register a scene?)`.

**Cause.** The page threw an uncaught error, or it has no runtime catalog. The usual reasons, most
common first:

* A `render` template has a single backslash, such as `\underline`, which is a syntax error.
* The runtime `<script>` comes after the `DeckTalk.scene` calls.
* The page does not load `decktalk-runtime.js`.

**Fix.**

1. Run `decktalk serve` and open the page from the URL it prints, with `?scene=N&hud=1`.
2. Read the error in the browser console.
3. Fix the page.
4. Record the section again.

**More.** [Record one section again](/guides/rebuild-one-section#record-one-section-again)

### `STALLED` in record

`record` prints `STALLED <ms>ms` and logs `frame stall(s) after narration t=0`, and
`recording section NN again`.

**Cause.** The machine was too busy to paint, so a reveal was recorded late. After narration t=0, the
page saw a gap between two frames longer than `[record] stall_ms` (150).

**Fix.**

1. Close other work, or use a less busy machine.
2. Record the section again.

`record` already tried again up to `[record] retries` (2) times. A long first draw under the cover,
before t=0, is not a stall.

**More.** [Record one section again](/guides/rebuild-one-section#record-one-section-again)

### `TRUNCATED` in record

**Cause.** The recording is shorter than requested by more than `[record] truncated_slack_seconds`
(0.5). Chromium was stopped, or the machine stalled.

**Fix.**

1. Record the section again.
2. Run `decktalk record --only N` again.

`build` exits 0 on `TRUNCATED`, so a script must read the `record` table or its JSON.

**More.** [Record one section again](/guides/rebuild-one-section#record-one-section-again)

### `NO COVER` in record

`record` logs `no magenta cover found; narration t=0 is a guess`, and the log's `t0_method` starts with
`NO COVER`.

**Cause.** `record` found no magenta cover in the first `[record] cover_scan_seconds` (4.0) of the recording,
so t=0 is a guess. The usual reasons:

* The page removes the cover itself.
* A page written by hand starts its clock at load instead of at `DeckTalk.startClock()`.

**Fix.**

1. Make sure that the page loads the packaged `decktalk-runtime.js`.
2. If the machine is very slow, raise `[record] min_cover_seconds`.
3. Record the section again.

Without a cover, `record` uses the first painted frame plus `settle_seconds`. If no frame is painted,
it uses 1.1 s plus `settle_seconds`.

`record` scans only the first `[record] cover_scan_seconds`. If narration t=0 comes later, `record` still
reports `cover`, and every reveal is late. If you raise `min_cover_seconds` near 4, raise `cover_scan_seconds` too.

**More.** [The handshake](/concepts/page-contract#the-handshake)

### `BLACK?` in record

**Cause.** The brightest luma at the middle of the recording (`MAX50`) is below `[record] black_ymax`
(40). Usually the page threw before it drew anything, or the slide is dark on purpose.

**Fix.**

1. Run `decktalk serve` and open the page from the URL it prints, with `?scene=N&hud=1`.
2. If the page is blank, read the browser console and fix the error.
3. If the slide is dark on purpose, do nothing. `BLACK?` exits 1 only with `--strict`.

**More.** [`decktalk record`](/reference/cli#decktalk-record)

### `KATEX ERROR` or `KATEX NOT LOADED` in record

**Cause.** The page's equations were never typeset, so the slide shows the source instead. Both are
certain findings, and `verify` reports them again from the recording log.

* `KATEX ERROR` follows `data-tex could not be parsed: "<tex>"`. A backslash inside a `render` template is single.
* `KATEX NOT LOADED` follows `KaTeX did not load within 5 s, so [data-tex] elements stay plain text`. The page's KaTeX tags point nowhere.

**Fix.**

1. If a value did not parse, write every backslash twice inside the `render` backticks. In a
   `<template data-slide>` a backslash is written once, which is why the starter's equation is markup.
2. If KaTeX did not load, check that `deck/katex/` holds `katex.min.js`, `katex.min.css`, and `fonts/`.
3. Run `decktalk doctor` and check that the `katex` row is `ok`.
4. In `deck/index.html`, use the two local tags below.
5. Record the section again.

```html theme={null}
<link rel="stylesheet" href="./katex/katex.min.css">
<script src="./katex/katex.min.js"></script>
```

The starter's `deck/index.html` loads KaTeX before the runtime, which is the order a page needs.

**More.** [Build in CI and offline](/guides/ci-and-offline)

### `no narration span yet` in record

**Cause.** A page section has no take in `build/narration/takes.json`, so it has no length to record.
Usually the script has no `## N.` heading for that section, or `narrate` has not run since you added it.

**Fix.**

1. If the heading is missing, add `## N. Title` to `script.md`.
2. Run `decktalk narrate`.

**More.** [Headings](/reference/script-md#headings)

### A section is early or late by a fixed amount

**Cause.** Nothing is printed. The recording's t=0 is wrong, so every reveal in the section is off by
the same amount.

**Fix.**

1. Open `build/recordings/NN.json` and read `t0_method`.
2. If it starts with `NO COVER`, follow [`NO COVER` in record](#no-cover-in-record).
3. Record the section again.

**More.** [Record one section again](/guides/rebuild-one-section#record-one-section-again)

## assemble

### `is missing, so a slate plays` in assemble

```text theme={null}
section 04: media/intro.mov is missing, so a slate plays for 4s. Drop your clip at that path. The section is optional, so --strict allows the slate
```

**Cause.** The file that `clip` names does not exist, so a titled slate plays for `slate_seconds`.

The section in the sample sets `optional = true`, so its warning ends with
`The section is optional, so --strict allows the slate`. A section without `optional` ends at
`Drop your clip at that path`.

**Fix.**

1. If you have the video, put it at the path in `clip`.
2. If the section sets `optional = true`, delete that line after the clip is in place.
3. If you do not want the clip, delete the section from both `decktalk.toml` and `script.md`.

**More.** [Add a clip section](/guides/clip-section)

### `clip missing` or `recording missing` in assemble

```text theme={null}
error[MISSING_INPUT]: section 4: clip missing: ~/clip-lesson/media/intro.mov. Put your clip at that path, or set optional = true on the section to play its slate under --strict.
```

**Cause.** `--strict` turns a missing recording into an error. It does the same for a missing clip,
unless the clip section sets `optional = true`. Either way the command exits 3 and writes no video.

**Fix.**

1. If a clip is missing, put the video at the path in `clip`.
2. If you want the slate for now, set `optional = true` on the clip section.
3. If a recording is missing, record the section again.

**More.** [Clip section keys](/reference/decktalk-toml#clip-section-keys)

### `using black` in assemble

**Cause.** A page section has no recording, so `assemble` plays black for that section.

**Fix.**

1. Record the section again.

**More.** [Record one section again](/guides/rebuild-one-section#record-one-section-again)

### `has no span in` in assemble

**Cause.** A page section has no take in `build/narration/takes.json`, so it has no span to cut to. The
script has no heading for it, or `narrate` has not run since you added it.

**Fix.**

1. If the heading is missing, add `## N. Title` to `script.md`.
2. Run `decktalk build`.

**More.** [Headings](/reference/script-md#headings)

### `music missing` in assemble

```text theme={null}
music missing (~/my-lesson/build/music/music.mp3), so there is no music. Run `decktalk soundscape` to make it
```

**Cause.** `[mix]` names a music or ambience file that does not exist. The video plays without
it. The starter has no `[mix]` sound keys at all, so this only follows a key you added.

**Fix.**

1. If you want the sound, run `decktalk soundscape` or put your own file at the path.
2. If you do not want it, remove the key from `[mix]`.

**More.** [Sound](/concepts/sound)

### `unresolved; skipped` in assemble

`assemble` logs `marker '<name>' unresolved; skipped` or `sfx <file>: cue '<cue>' in section <N> is unresolved; skipped`.

**Cause.** A marker's phrase or a sound effect's cue did not resolve, so `assemble` left it out.

**Fix.**

1. If a marker is skipped, check its `section` and `on` values in the markers file.
2. If a sound effect is skipped, check that `align` resolved its cue in that section.
3. Run `decktalk assemble`.

**More.** [`markers.json`](/reference/decktalk-toml#markers-json)

### `[loud] skipped` in assemble

```text theme={null}
[loud] skipped: the narration is a silent placeholder, so the soundtrack is encoded as it was mixed
```

**Cause.** A build without voice has a click track, not speech, so `assemble` skips the loudness pass. The
clicks keep their level so that `verify` can find them.

**Fix.** None is needed. A voiced build runs the loudness pass.

**More.** [Loudness](/concepts/sound#loudness)

### `true peak` or `integrated loudness` in assemble

`assemble` logs `[loud] true peak <X> dBTP is above the <Y> dBTP ceiling` or
`[loud] integrated loudness <X> LUFS is <D> LU from the <Y> LUFS target`.

**Cause.** After the loudness pass, the voiced mix misses a `[mix.loudness]` target. With `--strict`,
the miss is an error.

**Fix.**

1. Lower the music, ambience, or sound effect levels in `[mix]`.
2. Run `decktalk assemble`.

**More.** [`[mix.loudness]`](/reference/decktalk-toml#mix-loudness)

## verify

### `BLACK` in verify

**Cause.** The brightest luma 0.2 s after a section start is at most `[verify] visible_ymax` (60).
Nothing bright is on screen when the section starts.

**Fix.**

1. Make sure that the scene's first cued slide draws something at t=0.
2. If a dip to black is long on purpose, raise `[verify] after_dip_seconds`.

**More.** [Section starts](/reference/verify#section-starts)

### `SPEECH AT CUT` in verify

**Cause.** The narration in the last 0.15 s before a cut is louder than -40 dBFS. The voice was still
speaking when the picture cut.

**Fix.**

1. Raise the section's `tail_seconds`, or `[narration] min_tail_seconds`, to leave more silence after the last
   word.

If the section must stay short, end its script sooner instead.

Clip sections carry their own audio and have no cut row.

**More.** [Cuts](/reference/verify#cuts)

### `POP AT CUT` in verify

**Cause.** A section sets `seamless = true`, but its first frame differs from the previous section's last
frame by more than `[verify] max_pop_percent` (0.1 percent of the pixels). The picture jumps at the cut. The
usual reasons:

* The page opens in a different state than the previous page ended in, such as a slide that is not shown yet.
* An element sits at a different position or size in the two pages.
* The previous section's last slide fires after its narration ends, so its recording ends early.

**Fix.**

1. Run `decktalk screenshots --section N --at 0` for this section, and compare it with the last frame of the previous section.
2. Make the page's opening state match the previous page's final state.
3. If the jump is intended, remove `seamless` from the section.

**More.** [Cut continuity](/reference/verify#cut-continuity)

### `NO CHANGE` in verify

**Cause.** The picture did not change enough across the cue, or it changed no more than it was already changing.
The usual reasons:

* The reveal is very small or very slow.
* The reveal changes only color at the same brightness. `verify` compares luma only.

**Fix.**

1. Run `decktalk screenshots --section N --at S`, where `S` is just after the value in the `cue` column.
2. If the reveal is subtle on purpose, mark the cue `"verify": false` in `cues.json`.
3. If the reveal is slow, lengthen `[verify] probe_delays`.

A cue less than a second after another cue gets probes that fit the gap. `verify` logs `another cue is close`
for it. A slow reveal may not finish inside a short gap. Check such a reveal by eye.

**More.** [Change test](/reference/verify#change-test), [Close cues](/reference/verify#close-cues)

### `THIN CHANGE?` in verify

**Cause.** The cue passed the change test, but only just. Its `chg %` is below 0.3, or its `chg %` minus its
`ctl %` is below 0.3. Those limits are `[verify] thin_change_factor` (3) times the floors of 0.1. A slightly
smaller reveal, or a little more motion elsewhere, would read `NO CHANGE`. This is an uncertain finding, so
`verify` exits 1 on it only with `--strict`.

**Fix.**

1. Run `decktalk screenshots --section N --at S`, where `S` is just after the value in the `cue` column.
2. If the reveal is too small to see, make it larger or give it more contrast in the page.
3. If the reveal is small on purpose and plainly visible, leave it. Or mark the cue `"verify": false`.
4. To turn the warning off, set `[verify] thin_change_factor = 1`.

**More.** [Change test](/reference/verify#change-test)

### `OFF CUE` in verify

This example shows the format of an `OFF CUE` row.

The row keeps the shape of every other cue row, with `OFF CUE` in place of `changed`.

```text theme={null}
check                 cue       at   chg %   ctl %   offset     a/v  result
3:3.1make            8.71    38.87   10.85    0.00    -90ms  -118ms  OFF CUE
```

**Cause.** The picture changed, but the offset column is past 80 ms or the `a/v` column is past 120 ms.
The table prints no distance, so compare the columns with those limits.

* A negative offset column means that something moved before the cue. Often an earlier reveal is still animating, or a `data-delay` element appeared at the mount.
* A positive offset column of more than a frame or two means that the reveal has its own delay, such as a CSS transition.

**Fix.**

1. Run `decktalk screenshots --section N --at S` for times around the value in the `cue` column.
2. Find the early motion or the delay in the frames.
3. If the delay is intended, raise `[verify] max_offset_frames`.

If only the `a/v` column is past its limit, see [A large `a/v` value in verify](#a-large-a/v-value-in-verify).

**More.** [Onset and offset](/reference/verify#onset-and-offset)

### A large `a/v` value in verify

**Cause.** Nothing but `OFF CUE` is printed. The audio and picture in the final file are out of step,
or a louder sound effect sits near the cued word.

**Fix.**

1. Build with `decktalk build --no-voice --no-soundscape` to rule out the soundscape.
2. Open `build/recordings/NN.json` and check that `t0_method` starts with `cover`.
3. Record the section again.
4. Check that the offset column alone is small.

A build without voice puts a click at every word start, and the `a/v` column compares the picture with that
click. The click and the cue come from the same word time. A large value therefore points at the
recording or the mix.

**More.** [verify](/reference/verify#cues)

### `NO_CLICK` in verify

**Cause.** After a build without voice, `verify` found no click within `[verify] click_search_seconds` (0.25)
of the cued word's start. The `a/v` column shows `-`, and the JSON reason is `NO_CLICK`.

**Fix.** None is needed. `verify` still judges the cue on its offset column.

A `$end` cue gets `NO_CLICK` when its last word is longer than `click_search_seconds`. `$end` sits on
the end of the last word, and clicks sit on word starts.

**More.** [verify](/reference/verify#cues)

### `skipped` in verify

**Cause.** `verify` measured nothing for the cue. The reason follows the verdict, and a skipped row
never fails.

**Fix.**

1. Find the reason in [Skipped rows](/reference/verify#skipped-rows), and apply its fix.

**More.** [Results](/reference/verify#results)

### `malformed check` in verify

The error reads `malformed check '<text>'; want SECTION:CUE, e.g. 3:3.1draw`.

**Cause.** A cue argument does not start with a section number and a colon.

**Fix.**

1. Write each cue as `SECTION:CUE`, such as `8:7.1checked`.

**More.** [`decktalk verify`](/reference/cli#decktalk-verify)

### `nothing to verify` in verify

```text theme={null}
error[MISSING_INPUT]: the section videos and the final mp4 are not both there, so there is nothing to verify.
  hint: Run `decktalk assemble` first.
```

**Cause.** The project has no assembled video yet.

**Fix.**

1. Run `decktalk build --no-voice`, or `decktalk assemble` if the recordings exist.
2. Run `decktalk verify`.

**More.** [`decktalk build`](/reference/cli#decktalk-build)

### `is not a section in decktalk.toml` in assemble or verify

```text theme={null}
build/sections/09.mp4 is not a section in decktalk.toml, so verify ignores it. Delete the file if an earlier build left it.
```

**Cause.** `build/sections/` holds a section video whose number is not a section in `decktalk.toml`. An earlier build
usually left it before you renumbered or removed sections. `assemble` and `verify` read only the sections in
`decktalk.toml`, so the file changes nothing.

**Fix.**

1. If you renumbered or removed the section, delete the file.

**More.** [`build/out/`](/reference/artifacts#build-out)

### A typewriter reveal fails the cue check

**Cause.** Nothing but `NO CHANGE` is printed. An element with `data-text="type"` or `data-text="spoken"` appears a
little at a time, so the first probe sees too little change.

**Fix.**

1. Give the element's container a reveal that appears at once on the same cue, such as `data-reveal="fade"`.
2. Keep `data-text="type"` or `data-text="spoken"` on the text inside it.

**More.** [Slide recipes](/guides/slide-recipes)

## words and clip

### `The take index is not there` in words or clip

```text theme={null}
error[MISSING_INPUT]: The take index is not there, so no section has a start to cut from.
  hint: Run `decktalk narrate`, or `decktalk narrate --no-voice` to spend nothing.
```

The command exits 3.

**Cause.** The project has no narration yet, so no section has word times.

**Fix.**

1. Run `decktalk narrate`, or `decktalk build --no-voice`.
2. Run the command again.

**More.** [`decktalk words`](/reference/cli#decktalk-words)

### `has no section video` in clip

```text theme={null}
error[MISSING_INPUT]: section 1 has no section video at ~/my-lesson/build/sections/01.mp4. Run `decktalk assemble` first.
```

**Cause.** `clip` cuts from the section video that `assemble` writes, and the section has none.

**Fix.**

1. Run `decktalk build --no-voice`, or `decktalk assemble` if the recordings exist.
2. Run `decktalk clip` again.

**More.** [`decktalk clip`](/reference/cli#decktalk-clip)

### `is a clip section` in clip

```text theme={null}
error[CONFIG]: section 4 is a clip section.
  hint: Cut a clip from a page section.
```

**Cause.** The section number names a clip section. A clip section has no narration, so `clip` has no mp3 to cut.

**Fix.**

1. Run `decktalk status`. Each page section row names its page, and each clip section row starts with `clip`.
2. Run `decktalk clip` with the number of a page section.

**More.** [`decktalk clip`](/reference/cli#decktalk-clip)

### `cuts the word` in clip

```text theme={null}
section 01: the span cuts the word 'This' in two, so the words file leaves it out
section 01: the span cuts the word 'DeckTalk.' in two, so the words file leaves it out
seconds 1.08, words 1 | 0 certain, 2 uncertain finding(s)
```

**Cause.** `--start` or `--end` falls inside a spoken word. The clip keeps the part of the word inside the span, but the
words file has no entry for the word, so no caption shows it. It is an uncertain finding, so `clip` exits 1 only with
`--strict`.

**Fix.**

1. Run `decktalk words --only N` for the section.
2. Move `--start` before the word's start, or `--end` after the word's end, into the silence next to the word.
3. Run `decktalk clip` again.

**More.** [`decktalk words`](/reference/cli#decktalk-words)

## Pages in a browser

The page writes each warning to the browser console with the prefix `decktalk:`. During a recording,
`record` logs it as `[page] NN  <warning>`, where `NN` is the section number, and writes it to the
recording log under `warnings`.

### `data-text="spoken" text not found in the spoken words` in a page

**Cause.** The text of a `data-text="spoken"` element matches no run of the section's spoken words, so the
element appears whole. Usually the slide writes a number as digits and the script writes it as words.

**Fix.**

1. Compare the element's text with the sentence in `script.md`.
2. Make the words match. Case and punctuation do not matter.

**More.** [Runtime](/reference/runtime)

### `matches no element, handler, or slide` in a page

```text theme={null}
[page] 02  cue "2.1code" matches no element, handler, or slide
```

**Cause.** The cue fired, but no element has that `data-cue`, no handler is registered for it, and no
slide has that id. `align` catches this only when the id has left the page file entirely. An id that is
still a quoted literal somewhere in the page, such as a key of a slide's `preview` object, passes `align`
and warns here instead.

**Fix.**

1. Add `data-cue="<id>"` to the element that should appear.

**More.** [Cue mismatches](/concepts/cues#cue-mismatches)

### `unknown cue id` in a page

The warning reads `unknown cue id <id> (no slide id, owns list or preview object matches it)`.

**Cause.** The page received a cue in `?cues=` that no slide in the playing scene owns.

**Fix.**

1. Check the id against the slide ids and their `owns` lists.
2. Add the id to the `owns` list of the slide that should own it.

**More.** [Cue ownership](/concepts/page-contract#cue-ownership)

### `owns no cue` in a page

The warning reads `slide "<id>" owns no cue in ?cues=, so it never appears`.

**Cause.** In cue mode, a slide mounts at its earliest cue. This slide owns none of the section's cues.

**Fix.**

1. Add a cue for the slide to `cues.json`.

If a cue for the slide already exists, list its id in the slide's `owns` instead.

**More.** [Cue mismatches](/concepts/cues#cue-mismatches)

### A page is empty, or `window.__decktalk` is missing

**Cause.** Nothing is printed. The page does not load `decktalk-runtime.js`, or its runtime `<script>`
tag comes after the `DeckTalk.scene` calls.

**Fix.**

1. Load `decktalk-runtime.js` before any `DeckTalk.scene` call.
2. Run `decktalk serve` and open the page from the URL it prints, to check the scene index.

**More.** [Page contract](/concepts/page-contract)

## Speed

### A build is slow

**Cause.** Nothing is printed. `record` plays each page section in real time, and encoding adds more
time.

**Fix.**

1. If you changed one section, run `decktalk build --only N`.
2. For drafts, set `DECKTALK_VIDEO_PRESET=veryfast`, or `preset = "veryfast"` under `[video]`.
3. For the final video, go back to the default preset.

A build without voice of the starter took 64 s for its 44.88 s video. The machine was a MacBook Pro
with an Apple M5 Pro and 64 GB of memory.

**More.** [Rebuild one section](/guides/rebuild-one-section)

## Related

If no entry fits, open an issue. Include the output of `decktalk doctor` and the `[[section]]` tables
involved.

* **Look up an option or exit code:** [CLI](/reference/cli)
* **Read a verify table:** [verify](/reference/verify)
* **Ask a general question:** [FAQ](/help/faq)
