Docs/Skills and code/skill video-visual-production
.claude/skills/video-visual-production/references/troubleshooting.md
Troubleshooting: Find the line you saw; do the fix. Never fix a failure by editing a rule, a threshold or a check script. If you believe a rule is wrong, tell the user which rule and why.
Troubleshooting
Find the line you saw; do the fix. Never fix a failure by editing a rule, a threshold or a check script. If you believe a rule is wrong, tell the user which rule and why.
Lint (beatsheet-lint.js, rule ids)
| You see | Meaning | Fix |
|---|---|---|
STRUCT ... does not follow / spans do not cover | scene spans leave a gap or overlap | make each scene start at the previous scene's last word + 1; first at 0; last at the beat's last word |
STRUCT ... cue X word N outside span | a cue points at a word that is not in this scene | pick a word index inside the scene's span (edit.js words a b) |
STRUCT ... uses cue X which the scene does not define | a text entry names a cue that is not in cues | add the cue or change the text's cue (use enter for "from the start") |
H1 ... class missing / unknown scene type | set class to E, D, M or T; use a type from scene-catalogue.md | |
H2 ledger id ... has no row | the scene cites a fact not in ledger-<beat>.json | add the row (copy the claim, status and source from the script ledger) or remove the id |
H2 class D scene with no ledger row | a data scene needs a ledger row | add one |
H3 ... asset ... has no record | the asset was never fetched | run the build (assets stage) or assets.js --sheet <sheet> |
H3 ... without an on-screen source line | an evidence scene needs a text entry with role: "source" and the scene must draw it | add it |
H3 ... no allowed licence | Commons file is not CC0 / public domain / CC BY / CC BY-SA / GODL | choose another file |
H4 ... not allowed | news video, logo, face or stock | remove it; use a typographic scene |
H5 ledger row ... has status X | [CHECK] or [V-search] facts may not be drawn | do not draw it; tell the user |
H8 "X" is on screen but N is not spoken | a number on screen the voice does not say in this scene | remove it, or if the voice said it earlier in the beat add recall_from: <word index> |
H8 "N" is spoken but not shown on screen | the voice says a number the scene does not show | add a text entry with number: true on that word |
T1 "..." is held X s, needs Y s | text is on screen too briefly | cue it on an earlier word, shorten it, or start the next scene later (start_shift_frames) |
A3 sfx ... ms after / more than 3 sfx within 10 s | sounds too close or too many | remove or move one (edit.js sfx-remove) |
A4 sfx ... no role or event id | add role and event | |
Warning LABEL | a label has no word in the voice span | add spoken_as (the spoken word) or recall_from |
Warning SYNC ... interpolated word time | the cue word's time was estimated, not measured | accept it, or choose a neighbouring measured word; tell the user |
Warning L1, P3, A3 above 8 a minute | pacing or density | optional; mention to the user |
Build stages
| You see | Cause | Fix |
|---|---|---|
canvas is not installed | cd .claude/skills/video-visual-production/render && npm install | |
Node 22 or newer is needed | use Node 22+ | |
missing input ... | the audio, words or ledger path in the sheet is wrong | fix the path (repo-relative) |
FAILED bt2011...: anchor not found in page text | the article text does not contain your anchor words | open the page text (curl), copy the exact words; check the page did not move behind a paywall; if it still fails the scene is shown typeset and the build warns. Report that to the user |
highlight phrase(s) not found on page | a highlight phrase is not on the page exactly | copy it exactly from the page text |
Chrome did not expose a page target | Chrome path or port | set CHROME_PATH, close other Chrome windows started by the tool, retry |
REFUSED ...: licence ... is not in the allowlist | Commons licence | pick another file |
typeset: phrase not found in fallback text | fallback_text lacks a highlight phrase | make the fallback the full passage |
render.js ... Cannot read properties of undefined | a scene prop is missing | open scene-catalogue.md for the type and compare |
ffmpeg exited | ffmpeg problem or disk full | check free space on C: (needs about 500 MB per beat) |
mix says limiter cost about X dB with X over 4 | the voice is very peaky | tell the user; it still passes |
| build is slow (over 5 min) | machine busy (6 GB RAM) | close other programs; use --preview while authoring |
Check (STATUS-video-<beat>.md)
| Row fails | Meaning | Fix |
|---|---|---|
T2 text too small | a text box under the minimum size | raise size in the scene's props or edit the scene function |
T3 contrast | text colour against the sampled background | use a lighter tone (ink or muted), not dim; avoid effects drawn behind text |
T4 safe area / bottom 12% | text too close to the edge | move it or shorten it; for a fixed layout, edit the scene function |
T4 overlapping text | two text boxes overlap at the end of the scene | change positions or remove one line |
Q1 black / freeze / silence | undeclared | find which scene (contact-sheet.png), fix its animation |
Q3 no change at a cut | two scenes look identical across a cut | make the scenes different or use fade |
S2 a sound is off its event | the contact offset in contacts.js differs from the animation | align the animation in scenes.js to the contact constant |
A1 loudness / peak | mix outside -14 LUFS +-1 or peak above -1 dBTP | rerun the mix (mix.js); if it still fails tell the user |
Q0 frame count | the cache holds a stale clip | rebuild with --no-cache |
Lessons from beats B2 to B9 (what failed, and where the fix belongs)
Most check failures were defects in a scene type, not in a beat. Fix them once in the scene code (the orchestrator does this; storyboard and review agents may not edit code), then every beat benefits.
| What failed | Cause | Fixed in |
|---|---|---|
| T4 text past the right or left edge | a label wider than the room the scene left it | txt() in draw.js now keeps every text box inside the 96 px margins; race labels have 450 px; credits wrap |
| T3 contrast on red, dim or grey text | the dark red and the dim tone are too dark on the near-black ground | C.lossText (lighter red) for red text; dim tones map to muted; a darker red on the cream calendar |
| Q1 black frames at the end | a dark closing scene fades below the black threshold earlier than the last frames | the whole 0.6 s end fade is declared black in the timeline |
| Q3 "no change at a cut" on a scene that fades in | the first frames of a fade-in equal the previous scene's empty end | the check looks up to 12 frames past the cut |
| two series labels piled up | both end labels went above their points | higher series above, lower below; series names in a legend row under the chart |
| a falling line crossing its own value label | the label sat above a point the line arrives at from above | the label goes below when the line falls into the point |
| a slot lock blocked every build for 30 minutes | a killed build never released its lock | the lock records the owner's process id; a dead owner's lock is removed at once |
| alignment 63% on a 100 s beat | Whisper dropped 85 words in one long pass | align-beat.js retries with the large model, then cuts the beat into 30 s chunks at silences |
| ffmpeg could not cut the B3 WAV | odd timestamps: input-side seek returns nothing | decode first, then trim (output-side -ss) |
| shell tools ate backslashes in generated code | heredocs and node -e strings | write code with the Write or Edit tool; never patch regexes through the shell |
What a storyboard should avoid so the checks pass first time: put a number only on the word that says it; keep texts short enough to hold for 1.2 s; use say for numbers the voice speaks in words; do not draw a figure the voice does not say, even a derived one (the 197-day clock in B9 was dropped for this reason: a waiver or a spoken number is needed); give every data scene a source line.
When you are stuck
Stop after two failed attempts at the same step. Write down: the command, the exact error, what you tried. Tell the user. Do not work around a gate, and do not claim the build passed.
Show the raw text
# Troubleshooting
Find the line you saw; do the fix. Never fix a failure by editing a rule, a threshold or a check script. If you believe a rule is wrong, tell the user which rule and why.
## Lint (`beatsheet-lint.js`, rule ids)
| You see | Meaning | Fix |
|---|---|---|
| `STRUCT ... does not follow` / `spans do not cover` | scene spans leave a gap or overlap | make each scene start at the previous scene's last word + 1; first at 0; last at the beat's last word |
| `STRUCT ... cue X word N outside span` | a cue points at a word that is not in this scene | pick a word index inside the scene's `span` (`edit.js words a b`) |
| `STRUCT ... uses cue X which the scene does not define` | a `text` entry names a cue that is not in `cues` | add the cue or change the text's `cue` (use `enter` for "from the start") |
| `H1 ... class missing` / `unknown scene type` | | set `class` to E, D, M or T; use a type from `scene-catalogue.md` |
| `H2 ledger id ... has no row` | the scene cites a fact not in `ledger-<beat>.json` | add the row (copy the claim, status and source from the script ledger) or remove the id |
| `H2 class D scene with no ledger row` | a data scene needs a ledger row | add one |
| `H3 ... asset ... has no record` | the asset was never fetched | run the build (assets stage) or `assets.js --sheet <sheet>` |
| `H3 ... without an on-screen source line` | an evidence scene needs a `text` entry with `role: "source"` and the scene must draw it | add it |
| `H3 ... no allowed licence` | Commons file is not CC0 / public domain / CC BY / CC BY-SA / GODL | choose another file |
| `H4 ... not allowed` | news video, logo, face or stock | remove it; use a typographic scene |
| `H5 ledger row ... has status X` | `[CHECK]` or `[V-search]` facts may not be drawn | do not draw it; tell the user |
| `H8 "X" is on screen but N is not spoken` | a number on screen the voice does not say in this scene | remove it, or if the voice said it earlier in the beat add `recall_from: <word index>` |
| `H8 "N" is spoken but not shown on screen` | the voice says a number the scene does not show | add a `text` entry with `number: true` on that word |
| `T1 "..." is held X s, needs Y s` | text is on screen too briefly | cue it on an earlier word, shorten it, or start the next scene later (`start_shift_frames`) |
| `A3 sfx ... ms after` / `more than 3 sfx within 10 s` | sounds too close or too many | remove or move one (`edit.js sfx-remove`) |
| `A4 sfx ... no role or event id` | | add `role` and `event` |
| Warning `LABEL` | a label has no word in the voice span | add `spoken_as` (the spoken word) or `recall_from` |
| Warning `SYNC ... interpolated word time` | the cue word's time was estimated, not measured | accept it, or choose a neighbouring measured word; tell the user |
| Warning `L1`, `P3`, `A3 above 8 a minute` | pacing or density | optional; mention to the user |
## Build stages
| You see | Cause | Fix |
|---|---|---|
| `canvas is not installed` | | `cd .claude/skills/video-visual-production/render && npm install` |
| `Node 22 or newer is needed` | | use Node 22+ |
| `missing input ...` | the audio, words or ledger path in the sheet is wrong | fix the path (repo-relative) |
| `FAILED bt2011...: anchor not found in page text` | the article text does not contain your anchor words | open the page text (`curl`), copy the exact words; check the page did not move behind a paywall; if it still fails the scene is shown **typeset** and the build warns. Report that to the user |
| `highlight phrase(s) not found on page` | a highlight phrase is not on the page exactly | copy it exactly from the page text |
| `Chrome did not expose a page target` | Chrome path or port | set `CHROME_PATH`, close other Chrome windows started by the tool, retry |
| `REFUSED ...: licence ... is not in the allowlist` | Commons licence | pick another file |
| `typeset: phrase not found in fallback text` | `fallback_text` lacks a highlight phrase | make the fallback the full passage |
| `render.js ... Cannot read properties of undefined` | a scene prop is missing | open `scene-catalogue.md` for the type and compare |
| `ffmpeg exited` | ffmpeg problem or disk full | check free space on C: (needs about 500 MB per beat) |
| mix says `limiter cost about X dB` with X over 4 | the voice is very peaky | tell the user; it still passes |
| build is slow (over 5 min) | machine busy (6 GB RAM) | close other programs; use `--preview` while authoring |
## Check (`STATUS-video-<beat>.md`)
| Row fails | Meaning | Fix |
|---|---|---|
| `T2` text too small | a text box under the minimum size | raise `size` in the scene's `props` or edit the scene function |
| `T3` contrast | text colour against the sampled background | use a lighter tone (`ink` or `muted`), not `dim`; avoid effects drawn behind text |
| `T4` safe area / bottom 12% | text too close to the edge | move it or shorten it; for a fixed layout, edit the scene function |
| `T4` overlapping text | two text boxes overlap at the end of the scene | change positions or remove one line |
| `Q1` black / freeze / silence | undeclared | find which scene (`contact-sheet.png`), fix its animation |
| `Q3` no change at a cut | two scenes look identical across a cut | make the scenes different or use `fade` |
| `S2` a sound is off its event | the contact offset in `contacts.js` differs from the animation | align the animation in `scenes.js` to the contact constant |
| `A1` loudness / peak | mix outside -14 LUFS +-1 or peak above -1 dBTP | rerun the mix (`mix.js`); if it still fails tell the user |
| `Q0 frame count` | the cache holds a stale clip | rebuild with `--no-cache` |
## Lessons from beats B2 to B9 (what failed, and where the fix belongs)
Most check failures were defects in a scene type, not in a beat. Fix them once in the scene code (the orchestrator does this; storyboard and review agents may not edit code), then every beat benefits.
| What failed | Cause | Fixed in |
|---|---|---|
| T4 text past the right or left edge | a label wider than the room the scene left it | `txt()` in `draw.js` now keeps every text box inside the 96 px margins; `race` labels have 450 px; credits wrap |
| T3 contrast on red, dim or grey text | the dark red and the dim tone are too dark on the near-black ground | `C.lossText` (lighter red) for red text; `dim` tones map to `muted`; a darker red on the cream calendar |
| Q1 black frames at the end | a dark closing scene fades below the black threshold earlier than the last frames | the whole 0.6 s end fade is declared black in the timeline |
| Q3 "no change at a cut" on a scene that fades in | the first frames of a fade-in equal the previous scene's empty end | the check looks up to 12 frames past the cut |
| two series labels piled up | both end labels went above their points | higher series above, lower below; series names in a legend row under the chart |
| a falling line crossing its own value label | the label sat above a point the line arrives at from above | the label goes below when the line falls into the point |
| a slot lock blocked every build for 30 minutes | a killed build never released its lock | the lock records the owner's process id; a dead owner's lock is removed at once |
| alignment 63% on a 100 s beat | Whisper dropped 85 words in one long pass | `align-beat.js` retries with the large model, then cuts the beat into 30 s chunks at silences |
| ffmpeg could not cut the B3 WAV | odd timestamps: input-side seek returns nothing | decode first, then trim (output-side `-ss`) |
| shell tools ate backslashes in generated code | heredocs and node -e strings | write code with the Write or Edit tool; never patch regexes through the shell |
What a storyboard should avoid so the checks pass first time: put a number only on the word that says it; keep texts short enough to hold for 1.2 s; use `say` for numbers the voice speaks in words; do not draw a figure the voice does not say, even a derived one (the 197-day clock in B9 was dropped for this reason: a waiver or a spoken number is needed); give every data scene a source line.
## When you are stuck
Stop after two failed attempts at the same step. Write down: the command, the exact error, what you tried. Tell the user. Do not work around a gate, and do not claim the build passed.
Shown as it is in the repository at build time (2026-11-03); the home directory appears as <home>. If this page and the file differ, the file is right. Back to the catalogue.