Skip to main content

LERP Rive Documentation Errata Tracker

Last revalidated against the released runtime boundary: 2026-08-14 (2.40.0 Web line -> C++ runtime-v0.1.271; shader/GPU editor workflow remains Early Access only). Historical errata entries retain their original evidence dates. Last editor-validated errata batch: 2026-08-14 (Rive Beta 0.8.5390 build 5377, MCP-driven Tests-protocol runtime validation plus current event-reference analyzer probe) Initial errata tracker open date: 2026-04-26

Purpose: record LERP documentation inconsistencies, inaccuracies, and materially misleading gaps discovered while validating live Rive Editor behavior and current official Rive docs.

Rule for this file: only add an item when there is concrete evidence from at least one of:

  • Rive Editor compiler/runtime behavior
  • editor-validated live example behavior
  • current official Rive docs
  • local LERP docs contradicting each other

Status meanings:

  • confirmed: we have editor behavior or current official docs showing the LERP instruction is wrong/stale.
  • gap: the LERP statement is not necessarily false, but is incomplete or misleading enough to cause wrong implementation.
  • watch: likely issue, but needs one more editor validation or source check before treating as confirmed.
  • resolved: issue was fixed in LERP docs; keep entry for traceability with date/hash note.

Summary

Entries are ordered newest first (freshest updates at the top).

IDAreaStatusSeverityShort finding
LERP-ERR-018Event names and gamepad payloadsresolvedhighCurrent-facing pages used historical *Invocation wrapper names, extra PointerEvent fields, and a stale generic gamepad payload instead of the Editor-reference event names and connected/event/disconnected triad.
LERP-GAP-017Runtime surface/editor type boundaryresolvedhighAugust 14 MCP validation records runtime passes for matrix equality and ranged GPUBuffer writes, the failed GPUTextureView format access, analyzer-only event/audio coverage, and the remaining Context/library/GPU/Web boundaries.
LERP-GAP-016While loops / script timeout recoveryresolvedhighResolved in LERP on 2026-06-14 after a report from Rive Community user Avo: the submitted Exercise4_WhileCountdown file froze the Rive editor because its while energy > 0 do loop incremented steps but never changed energy, so init() never returned. Course now warns at the general while-loop section and inside the exercise itself.
LERP-ERR-015Path winding / fill ruleresolvedhighResolved in LERP on 2026-06-12 after a report from GitHub user belal-sweileh: Exercise 3's described point order wound the triangle counter-clockwise, which the current clockwise-fill renderer does not draw; exercise reordered and winding rule documented.
LERP-ERR-011Node protocolresolvedhighResolved in LERP on 2026-06-12: draw was documented as required for add-menu listing; editor-validated that draw-less Node scripts list, place, and run.
LERP-GAP-012Node advance semanticsresolvedhighResolved in LERP on 2026-06-12: documented the playback-vs-design-mode advance model — return false unsubscribes from the playback advance loop only, draw is independent, design-mode settle passes always pass seconds = 0 and ignore the return value.
LERP-ERR-013Vector deprecated usageresolvedlowResolved in LERP on 2026-06-12: glossary and environment exercise still taught deprecated vec:length() style despite the core-types deprecation table; switched to static Vector.* calls.
LERP-GAP-014Styling/Path API driftresolvedmediumResolved in LERP on 2026-06-12: PaintDefinition.gradient now typed (Gradient | false)?, Color.toFloat surfaced in core-types, Color documented as a raw packed number, Vector z/[3] documented, general path mutate-after-draw rule added, extract/positionAndTangent clamping and defaults documented.
LERP-ERR-010Exercise validator whitespace feedbackresolvedhighResolved in LERP on 2026-06-04 after a report from Rive Community user Avo: failed-answer boxes now preserve exact whitespace and warn on whitespace-only mismatches.
LERP-GAP-009Script save/recompile workflowresolvedmediumResolved in LERP on 2026-06-04 after a report from Rive Community user Avo: first-script onboarding now explains the blue save-state dot and save-to-recompile behavior.
LERP-GAP-008Node script placementresolvedmediumResolved in LERP on 2026-06-04 after a report from Rive Community user Avo: docs now describe Node scripts as artboard script nodes and clarify nesting does not pass parent NodeData or PathData into callbacks.
LERP-ERR-006PropertyList APIresolvedmediumResolved in LERP on 2026-05-12: list docs now use list.length + list[index] and include clear, removeAt, and removeAllOf.
LERP-ERR-007Runtime surface driftresolvedhighResolved in LERP on 2026-04-27: docs now include a pinned runtime compatibility baseline and corrected live/partial/not-exposed status for docs-labeled "Coming soon" APIs.
LERP-ERR-005Node pointer eventsresolvedmediumResolved in LERP on 2026-04-26: Node pointer-event docs now use event:hit() / event:hit(true) instead of boolean consume/propagate returns.
LERP-WATCH-004Property listenersresolvedmediumResolved in LERP on 2026-04-26: listener sections now warn to keep VM/property handles alive (or use anchored overloads) for long-lived listeners.
LERP-GAP-003ViewModel contextresolvedmediumResolved in LERP on 2026-04-26: context:viewModel() wording now describes immediate data-context behavior, with context:rootViewModel() explicitly documented for root/global state.
LERP-ERR-002Path Effect / PathCommandresolvedhighResolved in LERP on 2026-04-26: Path Effect examples now use string command-name comparisons and clarify CommandType is not a guaranteed global table in editor scripts.
LERP-ERR-001ListenerActionresolvedhighResolved in LERP on 2026-04-26: ListenerAction guidance now uses performAction(self, listenerContext) as preferred and marks perform legacy.

LERP-ERR-018 - Current Editor event names and gamepad triad

Status: resolved in LERP 2026-08-14

Severity: high

Reported by:

  • MCP comparison against the live Rive Editor built-in rive/artboards and rive/interfaces scripting references

Affected local docs before fix:

  • docs/api/events.mdx
  • docs/quick-reference.mdx
  • docs/rive/protocols/listener-action-protocol.mdx
  • docs/rive/protocols/node-protocol.mdx
  • docs/rive/protocols/node-lifecycle.mdx
  • docs/rive/runtime-compatibility.mdx
  • docs/rive/script-capability-matrix.mdx
  • docs/rive/script-types.mdx

What was wrong:

  • Current-facing event sections used the historical runtime wrapper names KeyboardInvocation, TextInputInvocation, FocusInvocation, ReportedEventInvocation, ViewModelChangeInvocation, and NoneInvocation instead of the current Editor names KeyboardEvent, TextInput, FocusEvent, ReportedEvent, ViewModelChange, and NoneEvent.
  • PointerEvent claimed previousPosition and timeStamp, which are not fields in the current Editor reference; type was documented as a plain string instead of the exact PointerType union.
  • Gamepad guidance collapsed all payloads into stale isGamepad() / asGamepad() / GamepadInvocation APIs with an axis0 field. The current reference uses three distinct connected, changed, and disconnected payloads.
  • ReportedEvent.delaySeconds was presented as current even though the current Editor ReportedEvent reference exposes no public fields. That property is evidenced only on the historical runtime ReportedEventInvocation wrapper.

Evidence:

  • rive/artboards returned the exact PointerType, PointerEvent, KeyPhase, event payload, gamepad field/method, and ListenerContext definitions.
  • rive/interfaces returned the exact optional Node callbacks gamepadConnected, gamepadEvent, and gamepadDisconnected.
  • A temporary Tests script covering all of those types, fields, guards, accessors, and Node callbacks recompiled with diagnostics []. The probe was removed; the original three LERP_130_Runtime_Surface_Tests cases still passed and final diagnostics remained clean.
  • Pinned runtime-v0.1.262 source confirms why older names exist: rive_lua_libs.hpp and lua_listener_invocation.cpp register the historical *Invocation Lua userdata wrappers, while that same source snapshot already implements the three-way gamepad surface.

Untested boundary:

  • The analyzer probe did not synthesize keyboard, focus, reported, view-model-change, or hardware gamepad events. It proves current Editor type acceptance, not runtime dispatch or released Web 2.40.0 behavior.

Fix implemented in LERP (2026-08-14):

  • Replaced all current-facing event signatures with the exact Editor reference.
  • Added full gamepad state/change fields, query methods, ListenerContext triad, and Node callbacks.
  • Moved old *Invocation names into explicitly historical/source-runtime compatibility notes and removed unsupported current-facing fields.
  • Updated the compatibility matrix and changelog so analyzer acceptance, source confirmation, runtime dispatch, and released Web claims remain separate.

LERP-GAP-017 - August 14 runtime surface validation and editor type-check boundary

Status: resolved in LERP 2026-08-14 (was gap)

Severity: high

Reported by:

  • MCP-driven live validation in a fresh Untitled composition, Rive Beta 0.8.5390 build 5377

Affected local docs before fix:

  • docs/api/core-types.mdx
  • docs/api/data-input.mdx
  • docs/api/data-values.mdx
  • docs/advanced/viewmodels.mdx
  • docs/rive/runtime-compatibility.mdx

Evidence:

  • The real Tests-protocol script LERP_130_Runtime_Surface_Tests recompiled successfully with final diagnostics [].
  • Vector.xyz, Vector.cross3, vector buffer writes, and Mat4.lookAt/Mat4.ortho composition and writeToBuffer all passed.
  • Mat2D and Mat4 equality passed live: identity matrices compared equal and translated matrices compared unequal.
  • ViewModel1 exposed fontProp: Property<Font>, imageProp: Property<Image>, and payload: Property<Blob>. Typed getBlob/getFont/getImage lookup, missing/null behavior, and Blob string/buffer/nil runtime writes passed after an explicit payload :: any cast.
  • Before that cast, the current editor analyzer rejected the string, buffer, and nil setters with Expected this to be Blob. The course now teaches this distinction instead of presenting the runtime-accepted forms as unconditionally type-safe.
  • Data.ViewModel1.new('name') returned nil; Data.ViewModel1.new() succeeded.
  • GPUBuffer:write(source, destinationOffset, sourceOffset, byteLength) executed successfully for a valid range; both source- and destination-overflow probes were rejected.
  • The runtime executed GPUBuffer/GPUTexture constructors even though the current Editor analyzer reported those names as unknown globals.
  • GPUTextureView.format was executed and failed with attempt to index userdata with 'format'.
  • A temporary exact-reference probe accepted the current event/gamepad surface and AudioSound:pause(), resume(), and play() with diagnostics []; this is analyzer evidence, not runtime dispatch/call evidence.

Untested boundaries:

  • Context:globalViewModel() and Context:globalViewModelNames() were not executed.
  • Imported-library blob/shader resolution was not executed.
  • Event/gamepad dispatch and the AudioSound transport calls were not executed.
  • The broader GPUCanvas/render-pass/shader lifecycle was not executed and remains Early Access material. The ranged GPUBuffer operation is the narrow live exception; GPUTextureView.format is a recorded live failure, not an untested surface.
  • Released Web 2.40.0 behavior was not established by this editor probe.

Why this matters:

The runtime source snapshot, editor analyzer, and runtime behavior do not form one undifferentiated availability claim. Learners need the editor-confirmed subset, the explicit Blob cast requirement, the narrow GPUBuffer pass, the GPUTextureView failure, and the still-unexecuted Context/library/broader-GPU/Web boundaries to remain visible when choosing APIs for a course exercise.

Fix implemented in LERP (2026-08-14):

  • Added this traceable errata entry and linked it from the 1.3.0 changelog.
  • Updated the core-types, ViewModel, data-value, and compatibility guidance with the Beta validation tier and the uncast Blob setter diagnostic.
  • Preserved preview/rollout-dependent wording for released Web, Context, imported-library, and broader GPU surfaces while recording the exact GPUBuffer pass and GPUTextureView failure.
  • Machine-readable validation evidence is retained with the 2026-08-14 release-suite run at evidence/rive-editor/rive-editor-validation-2026-08-14.json; it is release evidence, not a public course route.

LERP-GAP-016 - while-loop exercise could freeze affected editor builds

Status: resolved in LERP 2026-06-14 (was gap)

Severity: high

Reported by:

  • Rive Community user Avo, via LERP course report with frozen editor and unrecoverable .rev open path

Affected local docs before fix:

  • /Users/ivg/github/lerp/docs/fundamentals/control-flow.mdx (Exercise 4 while-countdown exercise)
  • /Users/ivg/github/lerp/docs/fundamentals/iteration.mdx (general while-loop warning was present, but did not mention init() startup blocking)

Evidence:

  • Binary text recovery from the reported ScriptCourse_Broken.rev found the Exercise4_WhileCountdown script fragments:
local energy = 3
local steps = 0

while energy > 0 do
steps += 1
end

print(`ANSWER: steps={steps},energy={energy}`)
  • No recovered script fragment changed energy inside the loop. Because energy stays 3, energy > 0 stays true forever, print() never runs, and init() never reaches return true.
  • Local Rive runtime source includes a timed scripting context with a default timeout and wraps Lua calls through a guarded pCall, so an editor freeze indicates an editor/runtime safety gap in the affected open/evaluation path rather than a normal recoverable script error.

Why this matters:

This is an easy beginner mistake in the exact exercise where students first practice while. In a vulnerable editor path, the result is not just a failed answer; it can make the editor or file open path unresponsive enough to require force quitting.

Fix implemented in LERP (2026-06-14):

  • Strengthened the Fundamentals -> Iteration infinite-loop warning to explain that non-ending loops run synchronously inside the active callback, and init() can block startup.
  • Added an Exercise 4 warning showing the exact unsafe loop shape and stating that the countdown variable must change inside the loop body.
  • Added a checklist item requiring the loop body to change energy.

Recommended upstream Rive fix:

  • Ensure all user-authored Luau execution paths in the editor are covered by a watchdog or interrupt budget, including file open/recovery, script top-level evaluation, init, advance, draw, listeners, converters, layout scripts, and path effects.
  • On timeout, mark the offending script asset or node as errored, skip it for the session, surface a diagnostic with the callback/script name, and keep the editor/file open.
  • Provide an "open with scripts disabled" recovery mode for files saved with broken user scripts.

LERP-ERR-015 - Exercise 3 triangle wound counter-clockwise renders invisible

Status: resolved in LERP 2026-06-12 (was confirmed)

Severity: high

Reported by:

Affected local docs before fix:

  • /Users/ivg/github/lerp/docs/rive/protocols/node-protocol.mdx (Exercise 3 starter point order)
  • /Users/ivg/github/lerp/docs/api/drawing.mdx and /Users/ivg/github/lerp/docs/quick-reference.mdx (no winding guidance)

Evidence:

  • Live editor validation (Rive Early Access, 2026-06-12) with a side-by-side probe (FillRuleProbe): the reporter's exact counter-clockwise triangle (top → bottom-left → bottom-right) rendered invisible while the clockwise twin rendered normally.
  • The Rive runtime ships a clockwise fill pipeline (FillRule::clockwise; see runtime shader/pipeline docs), consistent with the observed behavior.
  • The scripting API exposes no fill-rule control on Path or Paint (also answering the reporter's API question).
  • Follow-up object-model validation (same day): authored Fill components DO carry a fillrule property (key 40, enum nonZero/evenOdd/clockWise) settable in the inspector — fill rule is per-Fill in the editor data model, while script-drawn paths (which have no Fill component) are fixed to the clockwise rule. Runtime probes confirmed path.fillRule write throws, paint.fillRule and Paint.with({ fillRule = ... }) are accepted but silently ignored (Paint swallows unknown property writes), and a visual nested-contour ring test showed no even-odd effect.
  • Runtime-source confirmation (rive-runtime checkout, same day): ScriptedPathData::renderPath hard-codes m_renderPath->fillRule(FillRule::clockwise) at render-path creation (src/lua/renderer/lua_path.cpp:29) with no setter; the Paint binding's paint_set_value switch has no fillRule atom and paint_newindex ignores the unknown-key return, which is why unknown Paint writes silently no-op (reported upstream — typos like paint.thicknes = 4 also vanish silently); the same function shows gradient accepts any non-Gradient value as "clear" (so gradient = false works by falling through to nullptr); the same-frame path mutation rule is enforced with the literal error "Path was modified between draws in the same frame."; and Path Effect output paths inherit the host Fill's rule via path->rewind(source->isLocal(), source->fillRule()) (src/scripted/scripted_path_effect.cpp:48), making Path Effects the one scripting context governed by the authored fill rule.

Why this matters:

A beginner following the exercise exactly as written gets a compiling, running script that draws nothing — with no error pointing at winding order.

Fix implemented in LERP (2026-06-12):

  • Exercise 3 point order changed to clockwise; winding warnings added to the exercise premise, the Path API reference, and the quick reference.

LERP-ERR-011 - draw incorrectly documented as required for add-menu listing

Status: resolved in LERP 2026-06-12 (was confirmed)

Severity: high

Reported by:

  • Live editor validation (Rive Early Access, 2026-06-11): instrumented probe scripts placed and run via the Rive MCP

Affected local docs before fix:

  • /Users/ivg/github/lerp/docs/rive/protocols/node-protocol.mdx

Evidence:

  • A Node script with only init and advance (no draw) compiled with zero diagnostics, appeared in the add-to-scene menu, was placed as a script node (ScriptedDrawable), and ran init/advance normally.
  • LERP claimed "If draw is missing, the script will not appear in the add-to-scene menu" and quiz node-protocol-q1 keyed "init and draw" as the required minimum.

Why this matters:

Learners building logic-only scripts (timers, orchestration) were told to add an empty draw, which adds an unnecessary per-repaint call; and the false claim misdirects debugging when a script "doesn't show up."

Fix implemented in LERP (2026-06-12):

  • Requirements block, protocol table, AE/JS comparison row, common-mistake section, exercise premise, and quiz node-protocol-q1 updated: draw is optional and only needed for rendering.

LERP-GAP-012 - Node advance return semantics and design-mode clock undocumented

Status: resolved in LERP 2026-06-12 (was gap)

Severity: high

Reported by:

  • Live editor validation (Rive Early Access, 2026-06-11): paired probe nodes (advance returning false after 3 calls vs always true) instrumented with per-call prints, observed across design-mode interaction and ~10 s of playback

Affected local docs before fix:

  • /Users/ivg/github/lerp/docs/rive/protocols/node-protocol.mdx
  • /Users/ivg/github/lerp/docs/rive/protocols/node-lifecycle.mdx

Evidence:

  • Playback: the always-true node ticked at exactly 60/s with real accumulating delta; the false-returning node received zero advance calls while drawing ~600 frames in the same window — so return false unsubscribes from the advance loop and draw is fully independent.
  • Design mode: no advance loop and no clock exist; advance fired as settle passes with seconds = 0 on artboard-dirtying events (object moves, pointer over canvas, recompiles), and the false return was ignored across 200+ event-driven re-advances.
  • The Rive editor's own built-in example comment ("returns true if it needs to keep drawing") is contradicted by this behavior and was reported upstream.

Why this matters:

Time-based scripts that divide by seconds break at design time (seconds = 0); scripts that return false expecting drawing to stop are misdesigned; LERP previously never defined the Node advance return value at all.

Fix implemented in LERP (2026-06-12):

  • Added the "Playback vs Design Mode: The Two advance() Clocks" section to node-lifecycle and an advance-semantics admonition to node-protocol, including the practical guard rules.

LERP-ERR-013 - Deprecated Vector instance methods still taught outside core-types

Status: resolved in LERP 2026-06-12 (was confirmed)

Severity: low

Reported by:

  • Internal docs cross-validation against the editor scripting reference and Vector deprecation attributes (@[deprecated{ use = "Vector.*" }])

Affected local docs before fix:

  • /Users/ivg/github/lerp/docs/glossary.mdx (Vector entry used pos:length() / pos:normalized())
  • /Users/ivg/github/lerp/docs/rive/environment.mdx (exercise TODO used diff:length())

Why this matters:

api/core-types.mdx deprecates all seven Vector instance methods in favor of static Vector.* calls; teaching the deprecated style elsewhere contradicts the course's own guidance.

Fix implemented in LERP (2026-06-12):

  • Both occurrences switched to Vector.length(...) / Vector.normalized(...).

LERP-GAP-014 - Styling and Path API reference drift vs editor scripting reference

Status: resolved in LERP 2026-06-12 (was gap)

Severity: medium

Reported by:

  • Cross-validation of the editor's built-in scripting reference (rive/paint, rive/color, rive/vector, rive/path definitions) against LERP API pages, with runtime confirmation via Test-protocol scripts run in the Early Access editor (2026-06-11)

Affected local docs before fix:

  • /Users/ivg/github/lerp/docs/api/drawing.mdx
  • /Users/ivg/github/lerp/docs/api/core-types.mdx

Runtime-confirmed findings folded into this entry:

  • PaintDefinition.gradient is (Gradient | false)?; assigning false removes a gradient and also works on the live paint.gradient property (reads back nil).
  • Color.toFloat(color) exists (returns {r, g, b, a} in 0–1) but was only documented in the GPU shaders page; now surfaced in the core-types Color section.
  • Color is a plain Luau number (ARGB packed); raw literals like 0xFFFF0000 are valid and equal Color.rgb(255, 0, 0).
  • Vector has a read-only z component and [3] indexing (0 for 2D vectors; populated by 3D APIs such as Mat4:transformPoint).
  • The path mutation rule is general (any mutation after drawing requires waiting a frame), not just reset(); path:add accepts any PathData.
  • positionAndTangent clamps distance and returns a normalized tangent; extract clamps distances and startWithMove defaults to true.
  • Related upstream finding reported to Rive: the editor reference's Paint.with example uses Color.hex('#FF0066'), but Color.hex does not exist at runtime (type(Color.hex) is nil); LERP already used Color.rgb(255, 0, 102).

Fix implemented in LERP (2026-06-12):

  • All of the above documented in api/drawing.mdx and api/core-types.mdx with editor-validation notes.

LERP-ERR-010 - Exercise validator feedback collapsed meaningful whitespace

Status: resolved in LERP 2026-06-04 (was confirmed)

Severity: high

Reported by:

  • Rive Community user Avo

Affected local code before fix:

  • /Users/ivg/github/lerp/src/components/ExerciseValidator.tsx
  • /Users/ivg/github/lerp/src/components/ExerciseValidator.module.css

User-reported evidence:

  • In Fundamentals → Variables → Exercise 4, a submitted answer with extra spaces around the first pipe was correctly rejected by strict validation.
  • The failed-answer display rendered the submitted string in normal HTML whitespace mode, visually collapsing the repeated spaces.
  • Copying the displayed failed answer therefore produced a normalized-looking answer that could pass, making the original rejection impossible for the learner to diagnose.

Why this matters:

The validator must be strict enough to catch exact-output mistakes, but the feedback UI must show the exact submitted string. If the display changes whitespace, it can make a correct validation failure look arbitrary.

Correct guidance:

  • Preserve exact whitespace in displayed submitted and expected answers.
  • Warn learners when a failed answer only differs by spacing.

Fix implemented in LERP (2026-06-04):

  • ExerciseValidator now detects whitespace-only mismatches after collapsed-whitespace comparison.
  • Answer display blocks now use whitespace-preserving styling.
  • The failed-answer UI now warns that extra, missing, or tabbed spacing is the difference.

LERP-GAP-009 - First-script onboarding did not explain save-to-recompile

Status: resolved in LERP 2026-06-04 (was gap)

Severity: medium

Reported by:

  • Rive Community user Avo

Affected local docs before fix:

  • /Users/ivg/github/lerp/docs/getting-started/your-first-script.mdx

User-reported evidence:

  • Learners could paste or write a script, see the blue dot in the script tab, and then test without realizing the script had unsaved changes.
  • Without saving, the script is not recompiled, so new code may not affect the running artboard.

Why this matters:

The first scripting exercise is where learners form their debugging model. If they do not know that save triggers recompilation, they can chase code mistakes that are really editor-state mistakes.

Correct guidance:

  • The blue dot beside the script tab means unsaved changes.
  • Double-clicking/renaming from the Assets panel can autosave, and Cmd+S / Ctrl+S saves explicitly.
  • Saving recompiles the script and applies changes.

Fix implemented in LERP (2026-06-04):

  • Added an early save/recompile warning to the first-script exercise.
  • Added the save-state indicator screenshot.
  • Updated the first-script assignment to include saving before validation.

LERP-GAP-008 - Node script placement wording implied host-shape code access

Status: resolved in LERP 2026-06-04 (was gap)

Severity: medium

Reported by:

  • Rive Community user Avo

Affected local docs before fix:

  • /Users/ivg/github/lerp/docs/getting-started/your-first-script.mdx
  • /Users/ivg/github/lerp/docs/getting-started/how-rive-scripts-work.mdx
  • /Users/ivg/github/lerp/docs/rive/protocols/node-protocol.mdx
  • /Users/ivg/github/lerp/docs/rive/protocols/node-lifecycle.mdx
  • /Users/ivg/github/lerp/docs/rive/environment.mdx
  • Repeated exercise setup text across fundamentals, types, OOP, advanced, best-practices, and project pages

Local LERP evidence:

  • Several lessons and project pages instructed learners to "attach" or "drag" a Node script to a shape or group.
  • Other reference pages described Node lifecycle callbacks by protocol shape, not by a host-shape object, creating a misleading gap in the mental model.

Runtime source check used for the correction:

  • Runtime source snapshot checked on 2026-06-04: /Users/ivg/github/rive-runtime at a5e6bcf1 (working tree dirty).
  • scripted_object.cpp calls Node init with self and Context.
  • scripted_drawable.cpp calls Node draw with self and Renderer, with pointer callbacks receiving PointerEvent.
  • lua_scripted_context.cpp exposes context/data/assets/redraw methods, not an implicit parent-shape handle.
  • scripted_path_effect.cpp is the protocol that passes PathData and host node data into update.

Why this matters:

Learners could reasonably infer that nesting a Node script under a shape gives the script a special "this shape" API surface. That is not how the Node protocol is shaped. Nesting affects normal scene hierarchy behavior such as transform inheritance, opacity, and draw order; it does not pass parent NodeData or existing shape PathData into Node callbacks.

Correct guidance:

  • Add a Node script to the artboard so Rive creates a script node.
  • Position or nest that script node when hierarchy behavior matters.
  • Use a Path Effect script when the code needs the existing path data of a stroke/fill.
  • Use explicit references, inputs, or documented artboard lookup patterns when a Node script needs a specific scene node.

Fix implemented in LERP (2026-06-04):

  • Added an early save/recompile note with the save-state screenshot in your-first-script.mdx.
  • Reworded first-script and protocol setup steps from "attach to shape" toward "add to the artboard, position or nest the script node."
  • Added Node placement boundary notes to the Node protocol, lifecycle, script type, scene API, environment, and capability matrix pages.
  • Swept exercises and project pages for misleading attach/drag-to-shape wording.
  • Corrected related Path Effect examples that implied Node scripts receive existing path geometry through draw.

LERP-ERR-006 - PropertyList API docs were incomplete and inconsistent

Status: resolved in LERP 2026-05-12 (was confirmed)

Severity: medium

Affected local docs:

  • /Users/ivg/github/lerp/docs/advanced/viewmodels.mdx
  • /Users/ivg/github/lerp/docs/api/data-values.mdx
  • /Users/ivg/github/rive-runtime-docs/8.3-lua-api-reference.md as a comparison source

Local LERP evidence:

  • advanced/viewmodels.mdx documents list.count and list:item(index).
  • api/data-values.mdx documents list.length, list:push, list:pop, list:shift, list:insert, list:swap, addListener, and removeListener.
  • api/data-values.mdx does not document list:clear().
  • rive-runtime-docs/8.3-lua-api-reference.md documents .length and [index] access, not count / item(index).

Validation note:

  • This was validated using live examples in the Rive editor during the audit pass.
  • Confirmed behaviors: items:clear(), items:push(vm), and indexed iteration (items.length, items[i]) on a live PropertyList.

Why this matters:

The current list seeding pattern for editor-validation demos depends on clearing and repopulating a live list during init(). If LERP users follow the list.count / list:item(index) page, or cannot find clear(), they may implement list code that does not match the current editor/runtime API.

Correct guidance:

Use the current validated list surface:

local items = rootVM:getList("items")
if items then
items:clear()

local vm = Data.YourItemType.new()
-- set item properties
items:push(vm)

for i = 1, items.length do
local item = items[i]
-- read/write item props
end
end

Recommended LERP fix:

  • Replace list.count with list.length.
  • Replace list:item(index) with list[index].
  • Add list:clear() to the PropertyList API section.
  • Add a seeded Artboard List example using Data.X.new(), list:clear(), and list:push(vm).

Fix implemented in LERP (2026-05-12):

  • advanced/viewmodels.mdx now uses list.length and list[index].
  • api/data-values.mdx now documents clear, removeAt, and removeAllOf.
  • Related runtime baseline references were refreshed to the May 12, 2026 audit snapshot.

Validation reference:

  • Live editor examples run during this errata audit (no internal project naming required in course docs).

LERP-ERR-007 - Docs "Coming soon" markers drift from runtime reality

Status: resolved in LERP 2026-04-27

Severity: high

Affected local docs before fix:

  • /Users/ivg/github/lerp/docs/api/events.mdx
  • /Users/ivg/github/lerp/docs/api/system.mdx
  • /Users/ivg/github/lerp/docs/api/data-input.mdx
  • /Users/ivg/github/lerp/docs/rive/protocols/listener-action-protocol.mdx

What was wrong:

  • Several surfaces marked "(Coming soon)" upstream were already live in runtime (ListenerContext, KeyboardEvent, TextInput, FocusEvent, NoneEvent, etc.), but LERP did not expose a pinned compatibility mapping.
  • Some surfaces were documented as available in LERP without runtime status qualification (Output<T>, ViewModel.name), despite not being exposed in current C++ Luau bindings.

Evidence baseline used:

  • Runtime source gitHead (npm metadata): 5581955bf70d8976c60254de1a9c50c128e70582 (runtime line 2.37.4, published 2026-04-24 UTC)
  • Runtime release line (npm): 2.37.4 for @rive-app/canvas, @rive-app/canvas-lite, @rive-app/webgl2, and @rive-app/canvas-advanced
  • Docs snapshot source: https://rive.app/docs/llms-full.txt
  • Direct runtime binding checks in listener_invocation.hpp, lua_listener_invocation.cpp, lua_input.cpp, lua_audio.cpp, and lua_properties.cpp

Fix implemented in LERP:

  • Added /Users/ivg/github/lerp/docs/rive/runtime-compatibility.mdx with:
    • pinned runtime commit/date
    • live/partial/not-exposed matrix for docs-labeled "Coming soon" surfaces
    • repeatable re-audit workflow
  • Updated affected API and protocol docs to align wording and examples to that baseline.

LERP-ERR-005 - Node pointer event consumption should use event:hit(), not boolean returns

Status: resolved in LERP 2026-04-26 (was confirmed)

Severity: medium

Affected local docs:

  • /Users/ivg/github/lerp/docs/rive/protocols/node-lifecycle.mdx

Local LERP evidence:

  • The Pointer Events section says "Return true to consume an event, false to let it propagate."
  • The same file later correctly says old pointerDown(self, position: Vector): boolean is wrong and pointerDown(self, event: PointerEvent) is correct.

Why this is inconsistent:

  • Current local API docs for PointerEvent document event:hit() as the way to mark an event handled.
  • Current Rive docs also document event:hit() and event:hit(true).
  • Live drag/drop validation used event:hit() and no boolean return value from pointerDown / pointerMove.

Correct guidance:

Use:

function pointerDown(self: MyNode, event: PointerEvent)
if inBounds(self, event.position) then
event:hit()
end
end

Do not teach:

function pointerDown(self: MyNode, event: PointerEvent): boolean
return true
end

unless a specific legacy scaffold proves that path is still supported.

Recommended LERP fix:

  • Replace the "return true / false" wording with event:hit() / no hit call.
  • Keep the warning that pointerDown(self, position: Vector): boolean is an old pattern.
  • Add event:hit(true) as the translucent/pass-through variant.

Validation reference:

  • Live drag-node example validated in the Rive editor.

LERP-WATCH-004 - Property listener lifetime / GC warning is missing from LERP listener examples

Status: resolved in LERP 2026-04-26 (was watch)

Severity: medium

Affected local docs:

  • /Users/ivg/github/lerp/docs/api/data-input.mdx
  • /Users/ivg/github/lerp/docs/advanced/viewmodels.mdx
  • any LERP examples that call property:addListener(...)

Local LERP evidence:

  • data-input.mdx documents property:addListener(callback) and property:addListener(obj, callback).
  • It does not explain why the object/anchor form matters.
  • It does not warn that ViewModel/property handles obtained as locals can be garbage-collected while listeners are expected to remain active.

Current Rive docs evidence:

  • Current Rive PropertyTrigger docs warn that if a ViewModel or property is obtained in a local and listeners are added without storing the object elsewhere, it may be garbage-collected after the function returns.
  • They recommend either storing the ViewModel/property on self or passing an anchor object to addListener.

Validation impact:

A live coordinate-label script stores property handles on self:

self.xProp = vm:getNumber("x")
self.yProp = vm:getNumber("y")
self.zProp = vm:getNumber("z")

This is the safer pattern because the script adds listeners to those properties.

Correct guidance pending final validation:

When adding listeners to VM properties, prefer one of:

-- Option A: keep handles on self
self.vm = context:viewModel()
self.xProp = self.vm and self.vm:getNumber("x")
if self.xProp then
self.xProp:addListener(onChange)
end

or:

-- Option B: use anchor form when available
xProp:addListener(self, function(anchor)
-- ...
end)

Recommended LERP fix:

  • Add a warning near every addListener section.
  • Explain when to store VM/property handles on self.
  • Explain the anchor overload with a working example.

Validation reference:

  • Live listener and drag examples validated in the Rive editor.

LERP-GAP-003 - context:viewModel() wording is misleading for nested/list item contexts

Status: resolved in LERP 2026-04-26 (was gap)

Severity: medium

Affected local docs:

  • /Users/ivg/github/lerp/docs/advanced/viewmodels.mdx
  • /Users/ivg/github/lerp/docs/api/data-input.mdx
  • /Users/ivg/github/lerp/docs/rive/protocols/node-lifecycle.mdx
  • /Users/ivg/github/lerp/docs/rive/inputs.mdx

Local LERP wording:

  • advanced/viewmodels.mdx says to use context:viewModel() for the artboard's main ViewModel.
  • The same page says context:viewModel() gets the artboard's bound ViewModel.
  • The key takeaway says to use context:viewModel() for artboard-level shared data.
  • rive/inputs.mdx describes context:viewModel() as global data shared across all scripts.

Why this is misleading:

A live editor validation depends on a more specific behavior:

  • Inside an Artboard List item, the card-local script uses context:viewModel() to get the item-local ViewModel.
  • The same script uses context:rootViewModel() to get the root ViewModel.
  • That distinction is essential for list items. If context:viewModel() is treated as always root/global/artboard-level, the focus action is designed incorrectly.

Current Rive docs evidence:

  • Current Context docs say viewModel() returns the ViewModel bound to the node's immediate data context.
  • Current Context docs say rootViewModel() returns the root artboard data context.

Correct guidance:

Use this rule:

context:viewModel()
-> the ViewModel from the current node's immediate data context
-> in an Artboard List item, this is the item VM

context:rootViewModel()
-> the root artboard's data context
-> use this for root/global state such as selectedId or root lists

Recommended LERP fix:

  • Replace "main/global/artboard ViewModel" wording with "immediate data-context ViewModel".
  • Add an Artboard List example that reads an item VM with context:viewModel() and root VM with context:rootViewModel().
  • Avoid analogies like Redux/global context for context:viewModel() unless clearly scoped to root scripts only.

Validation reference:

  • Live Artboard List examples validated in the Rive editor.

LERP-ERR-002 - Path Effect examples use unavailable CommandType.* global

Status: resolved in LERP 2026-04-26 (was confirmed)

Severity: high

Affected local docs:

  • /Users/ivg/github/lerp/docs/rive/protocols/path-effect-protocol.mdx
  • /Users/ivg/github/lerp/docs/site-map.mdx describes the Path Effects API as including CommandType

Local LERP evidence:

  • The Path Effect template compares cmd.type == CommandType.moveTo.
  • Later examples compare against CommandType.lineTo, CommandType.quadTo, CommandType.cubicTo, and CommandType.close.
  • The troubleshooting section repeats the same CommandType.* pattern as the correct approach.

Editor evidence from live path-effect validation:

The path effect script threw errors including:

Unknown global 'CommandType'; consider assigning to it first
polygonPathEffectType 'unknown' does not have key 'type'
Cannot add indexer to table '{ read type: unknown }'

Current Rive docs evidence:

  • Current Rive docs document command type values as moveTo, lineTo, cubicTo, quadTo, and close.
  • Current examples print command.type and show output like moveTo and lineTo.
  • The docs describe CommandType as the type/category of a command, but the editor did not expose a usable global table named CommandType in this validation run.

Correct guidance:

Do not write this in editor scripts:

if cmd.type == CommandType.moveTo then
-- ...
end

Use the editor scaffold's actual type surface. In the currently validated path-effect workflow, compare against the string command names:

if cmd.type == "moveTo" then
path:moveTo(cmd[1])
elseif cmd.type == "lineTo" then
path:lineTo(cmd[1])
elseif cmd.type == "quadTo" then
path:quadTo(cmd[1], cmd[2])
elseif cmd.type == "cubicTo" then
path:cubicTo(cmd[1], cmd[2], cmd[3])
elseif cmd.type == "close" then
path:close()
end

Recommended LERP fix:

  • Replace CommandType.* examples with string comparisons unless Rive exposes an actual import/global in the current editor.
  • Add a note that CommandType in docs is a conceptual/API type name, not necessarily a global Luau table.
  • Add an editor-compile smoke test for every Path Effect snippet.

Validation reference:

  • Live polygon Path Effect example validated in the Rive editor.

LERP-ERR-001 - ListenerAction callback is stale

Status: resolved in LERP 2026-04-26 (was confirmed)

Severity: high

Affected local docs:

  • /Users/ivg/github/lerp/docs/rive/protocols/listener-action-protocol.mdx
  • /Users/ivg/github/lerp/docs/rive/script-capability-matrix.mdx
  • /Users/ivg/github/lerp/docs/rive/protocols/transition-condition-protocol.mdx indirectly references perform(self, pointerEvent) as the ListenerAction callback

Local LERP evidence:

  • listener-action-protocol.mdx defines ListenerAction<T> with required perform: (self: T, pointerEvent: PointerEvent) -> ().
  • The protocol table marks perform as required.
  • The knowledge check says the correct required callback is perform(self, pointerEvent).
  • script-capability-matrix.mdx lists ListenerAction required callbacks as perform.

Current Rive docs / editor evidence:

  • Current Rive docs for ListenerAction list both perform and performAction.
  • They state that perform is deprecated and prefer performAction(self, listenerContext).
  • A live editor example was validated using performAction(self: FocusWindowAction, _listenerCtx: ListenerContext).

Correct guidance:

Use this shape for new ListenerAction scripts:

function init(self: MyAction, context: Context): boolean
self.context = context
return true
end

function performAction(self: MyAction, listenerContext: ListenerContext)
-- action body
end

return function(): ListenerAction<MyAction>
return {
init = init,
performAction = performAction,
}
end

Do not instruct new work to use perform(self, pointerEvent) unless explicitly targeting a legacy scaffold.

Recommended LERP fix:

  • Update the ListenerAction protocol page to show performAction as preferred/current.
  • Mark perform as deprecated legacy.
  • Update quizzes and the capability matrix.
  • Explain the difference between PointerEvent and ListenerContext.

Validation reference:

  • Live ListenerAction example validated in the Rive editor.

Official Rive docs checked during this errata pass:

  • ListenerAction: https://rive.app/docs/scripting/api-reference/interfaces/listener-action
  • Context: https://rive.app/docs/scripting/api-reference/interfaces/context
  • Data Binding: https://rive.app/docs/scripting/data-binding
  • PropertyTrigger: https://rive.app/docs/scripting/api-reference/data-value/property-trigger
  • PathEffect: https://rive.app/docs/scripting/api-reference/interfaces/path-effect
  • PathData: https://rive.app/docs/scripting/api-reference/path/path-data
  • PathCommand: https://rive.app/docs/scripting/api-reference/path/path-command
  • CommandType: https://rive.app/docs/scripting/api-reference/path/command-type
  • Pointer Events: https://rive.app/docs/scripting/pointer-events
  • PointerEvent: https://rive.app/docs/scripting/api-reference/pointer-event
  • Docs machine-readable source for "(Coming soon)" diffing: https://rive.app/docs/llms-full.txt
  • Node: https://rive.app/docs/scripting/api-reference/node
  • Editor Data Binding Lists: https://rive.app/docs/editor/data-binding/lists
  • Editor Scrolling: https://rive.app/docs/editor/layouts/scrolling

Local LERP docs checked during this errata pass:

  • /Users/ivg/github/lerp/docs/rive/protocols/listener-action-protocol.mdx
  • /Users/ivg/github/lerp/docs/rive/script-capability-matrix.mdx
  • /Users/ivg/github/lerp/docs/rive/protocols/path-effect-protocol.mdx
  • /Users/ivg/github/lerp/docs/api/data-input.mdx
  • /Users/ivg/github/lerp/docs/api/data-values.mdx
  • /Users/ivg/github/lerp/docs/advanced/viewmodels.mdx
  • /Users/ivg/github/lerp/docs/rive/inputs.mdx
  • /Users/ivg/github/lerp/docs/rive/protocols/node-lifecycle.mdx
  • /Users/ivg/github/lerp/docs/rive/protocols/layout-protocol.mdx
  • /Users/ivg/github/lerp/docs/rive/runtime-compatibility.mdx
  • /Users/ivg/github/rive-docs/editor/data-binding/lists.mdx
  • /Users/ivg/github/rive-docs/editor/layouts/scrolling.mdx
  • /Users/ivg/github/rive-docs/editor/layouts/layout-parameters.mdx
  • /Users/ivg/github/rive-runtime-docs/8.3-lua-api-reference.md
  • npm metadata check (npm view @rive-app/[email protected] gitHead) returning 5581955bf70d8976c60254de1a9c50c128e70582
  • npm metadata check (npm view @rive-app/[email protected] gitHead) returning 2833de372c0d22596494c89c328008ce5b1106d7

Append Procedure

When a new inconsistency appears:

  1. Add it to the summary table.
  2. Add a detailed section with local source, observed failure, current correction, and validation evidence.
  3. Mark it confirmed, gap, or watch.
  4. If the correction depends on current Rive docs, add the URL to Source Links Checked.
  5. If the item is later resolved in LERP, keep the entry and mark it resolved in LERP <date/hash>.