Troubleshooting
Cross-chapter pitfalls and error messages, grouped by symptom.
Keys do not respond
- Providers nested the wrong way:
KeyboardProvidermust sit insideScenarioManagementProvider; reversed, the keyboard silently stops working. - Wrong ownership: a binding is matched only while its owner (screen / layer element) is active, see Binding Attribution & the Owner Stack.
- Blocked by
mode/when: check the binding'smodeandwhen, see Modes and Intermediate Binding. - Modal takeover: a modal layer intercepts every key by default; reopen the ones you need with
allowModal, see Passing Keys with allowModal.
Layers and propagation
penetrationpasses keys through, it does not block them (an easy misread); usestopto block. See Passing Keys Through and Stopping Key Propagation.- Cross-page layers are reclaimed: non-persistent layers and modal layers (
crossPage: false) are removed onskip/back/gotoScreen(handled in the reducer).
Error boundaries
_dispatchis set in auseEffect, so it is unavailable duringcomponentDidCatch— calling layer / modal functions from an error boundary yieldsnull.
Testing
- Escape (
\x1b) is unreliable underink-testing-library;stdin.write's escape-sequence behavior is unstable.
Types
useRef<<T>parses as JSX in TSX — you must writeuseRef<T>.
Common error messages
| Message | Cause |
|---|---|
[ink-cartridge] No KeyboardEngine is mounted. …getEngine()… | A module-level API call ran before KeyboardProvider mounted |
[ink-cartridge] Condition "<id>" is not registered. … | A named condition was used without addCondition first |
[keyboard-engine] <fn>() must be called inside a screen component or overlay. | stop / penetration etc. called with no owner |
… declared in globalKeys with cover: false, so overriding is not allowed. | A cover: false global key was overridden |
