Error Codes

Reference for every WF-* error code, for the TH-* codes of the Threads extension, for the 3D-* codes of the Trillium extension, and for the KN-* codes of the Kinetic extension. Click a category button below to filter. A Kinetic error in the production build has a short message, the code, where the error came from and a link to its entry here, and each Kinetic entry describes the longer message of the development build.

Reading diagnostics from code

Development builds keep every WF-* diagnostic they print as data as well, so a test, a headless browser or an AI agent checking its own work can read them without parsing the console. wildflower.diagnostics holds them newest last, keeping the newest 200, and each is also dispatched as a wildflower:diagnostic event on document. Clear the list with wildflower.diagnostics.length = 0. Production builds have neither.

document.addEventListener('wildflower:diagnostic', (e) => {
    const { code, severity, message, context, suggestion, docs } = e.detail;
    myOverlay.add(code, context, suggestion);
});

// After exercising the page:
const problems = wildflower.diagnostics.filter((d) => d.severity === 'error');

An extension reports its own coded diagnostic with wildflower.diagnostic(). It prints the line in the format the framework uses, [KN-110] message: context, followed by the Suggestion and Docs lines, adds the entry to wildflower.diagnostics, dispatches the event and returns the entry. The Docs link is built from the code, so it points at that code's entry on this page. The method exists only in development builds, so an extension checks for it and prints its own line when it is absent.

if (typeof wildflower.diagnostic === 'function') {
    wildflower.diagnostic({
        code: 'KN-110',
        severity: 'warning',          // or 'error'
        message: 'The composition root has no data-duration',
        context: '#intro',
        suggestion: 'Give the root a data-duration in seconds'
    });
}

Showing:

WF-001 Root element not found
Core

Framework initialization cannot find the specified root DOM element. Check that your mount target exists in the HTML before calling wildflower.start().

WF-002 Invalid configuration value
Core

A configuration attribute carries a value the framework does not recognize, so the default is used instead. The warning gives the attribute and the accepted values (for example data-error-handling accepts log, throw, or silent). The Attribute Reference lists every accepted value. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-003 Capability excluded from this build tier
Core

A definition or markup uses a capability this build tier excludes, so it silently does nothing: tick() defined in a build without the frame loop (pool module excluded), or scoped-slot read bindings in the nano tier. Switch to a build that includes the capability, or remove the usage. The warning identifies the specific capability and the tier boundary. The Distribution Bundles table maps capabilities to tiers. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-101 Error initializing component
Components

A component's init() method or setup logic threw an exception. Check the browser console for the underlying error. The Error Boundaries page covers onError and the handling chain.

WF-102 Component instance not found
Components

Attempting to access a component that doesn't exist in the registry. Verify the component name matches its data-component attribute and that it has been registered. The Components page covers registration and matching.

WF-103 Component context not available
Components Context

A component's context object is missing when required. This usually indicates the component was destroyed or not fully initialized.

WF-104 Error in parent event handler
Components Actions

A parent component's event handler threw an exception when invoked by a child. Check the parent's method for errors.

WF-105 Manual DOM write on an engine-owned node
Components

A component called .text() on a data-bind node, .html() on a data-bind-html or data-list node, or .remove() on a managed node through the $el() helper. The engine keeps those nodes current, so the manual write will be overwritten by the next update or leave the engine tracking a removed node. Update state instead and let the binding do the writing. Unmanaged nodes stay free, and .val() on a data-model input is the sanctioned bridge. The DOM Helpers page draws the whole boundary. Warning severity (console.warn, never throws); fires when debug mode is on (the default in development builds).

WF-106 destroy() with the element still in the document
Components

destroyComponent() ran while the component's element was still connected, so the next scan will auto-resurrect it as a fresh instance with init() re-fired. For a real teardown remove the element as well (instance.element.remove()); if you wanted a reset, re-initialize state instead. Removing the element without calling destroy is always safe: the engine garbage-collects instances whose elements leave the document. The destroyComponent section shows the correct pair. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-107 Declared provider never provided
Components

A definition lists a key in uses: that nothing ever registered, so the $name accessor is never attached and reads of it are undefined. Register the provider with wildflower.provide('name', value) before components that use it initialize, or fix the key if it is a typo. The Service Providers section shows the registration. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-108 Directive or plugin registration overwritten
Components

A directive or plugin was registered under a name that already exists, and the new registration replaced the original. If both registrations are intentional (hot reload, deliberate override) the warning can be ignored; otherwise rename one. Note the asymmetry with components and stores, where a conflicting re-registration keeps the original instead (WF-215). The Plugins page covers directive registration. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-109 Named plugin exposes nothing
Components

A named plugin declares no state, computed, methods, top-level functions, or install, so it does nothing and wildflower.$name is never created. A plugin with only install (one that registers a directive, say) is valid, and this warning does not fire for it. Add a state:/computed:/methods: block, or declare functions directly on the plugin definition (same shape as components and stores). The Plugins page covers the plugin shape. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-201 Error evaluating computed property
State

A computed property function threw an exception during evaluation. Check that all referenced state properties exist and have expected types. The Computed Properties page covers the evaluation contract.

WF-202 Circular dependency detected
State

Two or more computed properties reference each other, creating an infinite loop. Restructure your computed properties to break the cycle. The Avoiding Circular Dependencies section shows the restructure.

WF-203 Error setting state value
State

The reactive proxy's set trap encountered an error. This can happen when assigning invalid values or when the state object has been corrupted.

WF-204 Error deleting state value
State

The reactive proxy's delete trap encountered an error when removing a state property.

WF-205 Error loading state from storage
State

Failed to read persisted state from localStorage or sessionStorage. The stored data may be corrupted or the storage quota exceeded. The Built-in Persistence section covers storageKey and autoSave.

WF-206 Error saving state to storage
State

Failed to persist state to localStorage or sessionStorage. Check that the storage quota has not been exceeded and that the data is serializable. The Built-in Persistence section covers the surface.

WF-207 Invalid parameter for state update
State

A state update method received an argument of the wrong type. Verify you're passing the correct data type.

WF-208 Computed property does not exist
State

Retired in v1.3. This code was reserved for reads of undefined computed properties but never fired from any code path. Misspelled computed and state references are caught by binding validation in development builds, which warns with a did-you-mean at bind time. The code number is not reused.

WF-209 Computed property must be a function
State

A computed property was defined as a value instead of a function. Computed properties must be functions that return a value. The Computed Properties page shows the function form.

WF-210 Invalid path segment
State Bindings

A dotted path like user.profile.name contains an invalid segment. Check for typos or undefined intermediate objects.

WF-211 Error in subscription callback
State Stores

A user-provided subscription callback threw an exception. Check the function passed to subscribe(). The Communication page covers subscriptions.

WF-212 Pool aggregate read inside a computed
State

Retired in v1.3. Pool aggregates (pool.length, pool.size) are reactive on demand as of v1.3: a computed reading them re-evaluates when entities are added, removed, or cleared, so the trap this warning guarded no longer exists and the warning was removed. On v1.2 and earlier, aggregates bypass reactivity and a computed reading them evaluates once and goes silently stale; the workaround there is mirroring the count into reactive state inside a tick(). The code number is not reused.

WF-213 Watch/subscribe path targets a list item by numeric index
State

A watcher or subscription registered a path like items.0.name. This is an anti-pattern: reactivity tracks items by object identity, so the index in a change path reflects the item's position when it was first observed, so after a splice, removal, or reorder the watcher fires for the wrong slot or goes silent. Watch the array (or a computed over it) and track items by id instead, e.g. watch: { items() { ... } } or a computed like activeItem() { return this.items.find(i => i.id === this.selectedId) }. The Communication page covers the watch block. A query store does not warn: every fetch and write replaces its rows as a whole, so store:user.rows.0.name always follows the current first row, which is the natural way to watch a single-record query. Warning severity (logged via console.warn, never throws); dev-mode only, stripped from production builds.

WF-214 Zero-arg computed in a list row reads an item property via this
State Bindings

A computed referenced inside a data-list row template was declared with no parameters, and its body reads this.<prop> where <prop> is not on the component's state or computeds but is a property of the current list item. Zero-arg computeds evaluate at component scope, so that read silently resolves undefined. Item-level computeds receive the item as their first argument: declare it, e.g. priceLabel(item) { return '$' + item.price }. A zero-arg computed that reads only component state is legitimate inside a row and never triggers this warning. The Item-Level Computed Properties section teaches the parameter rule. Fires once per component and computed; warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-215 Component or store re-registered with a different definition
State Components

A component or store was registered under a name that already exists, and the incoming definition differs from the stored one. Registration is first-write-wins, so the original is kept and the new definition is ignored. This is almost always an accidental collision (two components sharing a name, a hot reload without teardown). To replace a definition intentionally, call wildflower.unregister('<name>') first, then re-register; or give the new one a distinct name. The comparison hashes method source, so two definitions that share method names but differ in a method body are still flagged; an identical re-registration (the same definition scanned twice) does not warn. The Replacing a Definition section covers the intentional path. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-216 State property read thousands of times per frame (sustained hot loop)
State

One state property is being read through the reactive facade at hot-loop rates, sustained across animation frames. A facade read costs roughly 100x a plain property read; that is proxy physics, and every fine-grained framework pays it. The fix is one line: hoist the value to a local before the loop (const speed = this.state.speed) and read the local inside it. For per-entity hot data, pool entities are plain objects with zero proxy cost. One-shot sweeps (building a large structure once during init) do not trigger this warning; it fires only for reads that recur frame after frame, and only once per property name. The Architecture Patterns section shows where hot-loop state belongs. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-217 Computed wrote to state during evaluation
State

A computed mutated tracked state while it was being evaluated, including an in-place sort or reverse. Computeds must be pure. The mutation invalidates the computed while it runs, and anything bound to it (a data-list, a binding) can silently render empty or stale. Copy before mutating (return [...items].sort(...)), or move the write into a method. (The framework's own internals write through an untracked escape and stay silent; that escape is not part of the application surface.) Warned once per computed. The Chaining, Filtering & Sorting section shows the copy-before-sort idiom. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-218 Name collision across definition buckets
State

The same name is defined in more than one bucket of a definition: a method colliding with a state key or a computed, or a key defined in both state and computed. One of them is shadowed wherever the bare name resolves. For state/computed collisions the computed wins everywhere except explicit this.state.key reads. Rename one. Warned once per definition. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-219 Definition key ignored
State

A key in a component, store, plugin, or pool entity definition is not part of the definition contract, so it was ignored. At the top level that is a key that is not a function: state values belong inside state: {}, and only methods live at the top level, never in a methods or actions block. Underscore-prefixed keys are the documented stash and stay silent. Inside a component's root: block it is a key other than class, attr, style, show and directives (render included), a value that is not an expression string, a group that is not an object, or a directive name that is not registered; the message gives the nested key, as in root: class.open. Warned once per definition with a hint for the specific key. The Store Definition Mistakes section shows the correct shape. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-220 Assignment to a computed property
State

Computed properties are read-only derived values. The assignment was ignored and the property keeps computing from its inputs. Store the value in state instead, or rename the computed if you meant a state field. The Computed Properties page draws the derived-vs-stored line. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-221 batch() called without a function
State

wildflower.batch(fn) groups writes into one flush and requires a function argument; the call was a no-op. Pass the writes inside a function: wildflower.batch(() => { ... }). The write-to-DOM section shows batching in context. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-228 Validation rule declaration invalid
State

A rules: entry could not become a working rule and was disabled. String checks must parse and every variable they reference must be a state or computed name; the context states the specific problem, including any unknown variable, so a typo cannot silently gate a form. Per-field validation and the other rules keep working. Dev-mode warning at first validation. The Cross-Field Rules in Depth section states the contract.

WF-229 Validation rule check threw
State

A function-form rule check raised an exception, so the rule was skipped for that validation pass rather than blocking or passing the form on an error. The other rules and per-field validation still ran. Fix the check; the warning includes the rule name and the error message, once per rule. Dev-mode only. The Cross-Field Rules in Depth section covers the check contract.

WF-234 Validation rule returned something that is not a verdict
State

A function-form rule must return a value that reads as true when the rule holds and false when it does not. Three returns are never a verdict and each one fails quietly in its own direction, so the rule is skipped and named instead of coerced. A promise is always truthy, so an async check would report success no matter what it eventually resolved to; rules run synchronously, and work that has to wait belongs in the submit handler. undefined is always falsy, usually a missing return, and would block the form permanently. A string is truthy, so it reports success even though it reads like a failure message. Return true when the rule holds and put the wording in the rule's name or its message:. Dev-mode only, once per rule. The Cross-Field Rules in Depth section states the verdict contract.

WF-235 Item-level list computed returned a Promise
State Bindings

Computeds may return a promise at component, store, and plugin scope, where the entity tracks the request and updates the binding when it lands. A computed that takes the item as a parameter runs per row through the list renderer instead, which does none of that tracking. The promise never resolves into the row, so the binding renders the promise object as text, and a list of N rows would issue N uncoordinated requests. Item-level computeds must be synchronous. Fetch the collection once in a component-level computed, derive the row array from it in a second computed, and bind the list to the derived array; the Async Computed Properties page shows the pattern. Fires once per component and computed; warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-236 An underscore key in state is not reachable as this._name
State

A name that starts with an underscore on this is a raw instance field, the place for handles such as an AudioContext or an observer. The context passes it straight to the instance and never to state. A key like _since declared in state: {} still lands in state, but this._since reads the instance field, which starts undefined. Only this.state._since reaches the declared value. Drop the underscore to make it ordinary state, or leave it out of state and set this._since = 0 in init() for a non-reactive field. Applies to components, stores and plugins. Fires once per key when the definition is first used; warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-237 A list-row binding calls an item-level computed
State

Inside a list row, the name of an item-level computed (one declared with an item parameter, such as spent(item)) already stands for its value for that row. A binding that calls it, such as data-bind="spent(item) + 1", therefore calls a number, and the binding renders nothing. Name the computed instead (data-bind="spent"). For arithmetic on it, write another item-level computed that calls it, spentPlus(item) { return this.spent(item) + 1; }, and bind that. See Computed Properties. Fires once per component and computed, before the list first renders; warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-238 Reactive updates did not settle
State

One round of reactive updates ran a million updates without settling, so the framework stopped it. Something keeps waking itself: usually an effect, a watcher or a subscribe callback that writes state it also reads, directly or through another one that writes back. Dev builds name the computeds the looping updates read, which points at the cycle. Find the write that feeds back and guard it, for example by writing only when the value actually changes. Updates resume with the next change. Error severity (console.error, never throws); fires in every build, and only dev builds add the names.

WF-301 Error resolving data in context
Context Bindings

Context data resolution failed during binding evaluation. The bound property path may be invalid or the data structure has changed unexpectedly.

WF-302 Missing component instance in context
Context Components

A context operation requires a component instance but none is available. The component may have been destroyed.

WF-303 Error updating context
Context

The context update process encountered an exception. This is typically caused by invalid data or a corrupted context state.

WF-304 Error in context dependency notification
Context

Failed to notify dependent contexts of a data change. A dependent binding or computed property may have an error.

WF-401 Template not found for list
Lists

A data-list element resolved no item template. The development build reports the cause it found: row markup written as direct children instead of inside a <template>, a container inside <svg>, where the HTML parser never creates a real template and the element survives as inert with no content fragment, or no template in any searched source (inline child, data-use-template reference, inherited templates). Add a <template> child. For repeated SVG primitives use a data-pool, whose template does work inside <svg>; failing that bind a fixed set of elements. The template rule section states the requirement.

WF-402 Error rendering list
Lists

The list rendering process threw an exception. Check that the list data is a valid array and that template bindings reference valid item properties.

WF-403 Error updating list item
Lists

Updating an existing list item's bindings failed. The item data may have an unexpected structure.

WF-404 Error removing list item
Lists

Removing a list item from the DOM failed. The element may have already been removed or detached.

WF-405 Error in append optimization
Lists

The optimized append path for adding items to the end of a list encountered an error. The framework will fall back to a full re-render.

WF-406 Error in swap optimization
Lists

The optimized swap path for reordering list items encountered an error. The framework will fall back to a full re-render.

WF-407 Error in sparse update optimization
Lists

The optimized sparse update path (updating a subset of list items) encountered an error. The framework will fall back to a full re-render.

WF-408 data-pool container name is not in the component's pools block
Pools Components

A data-pool container names a pool that does not appear in the component's declared pools: {} block. Pool names must match exactly; code that populates getPool('items') never reaches a container written as data-pool="itmes", so the container renders nothing. This is almost always a typo, and the warning suggests the closest declared name. When the name belongs to a store's or plugin's pool instead, the warning identifies that store or plugin: their pools hold data only and cannot be drawn by a container (see Pools in a Store). A markup-only pool (no declaration, populated programmatically by the exact same name) is legitimate and stays silent. The data-pool section covers name resolution. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-409 Pool has a container but nothing added yet
Pools Components

A data-pool container and its template are wired correctly, but nothing had been added to the pool by the time the page settled. If the pool fills later by design, for example on user interaction or once a game starts, nothing is wrong and this note can be ignored; it fires once and only in development builds. Otherwise nothing renders: populate the pool from the component with this.getPool('name').add({ id: 1, ... }) or through the pools.name handle. The pool insertion section shows the population calls. When the container points at a store's or plugin's pool (data-pool="bees" for a store's bees) or holds a path (data-pool="$swarm.bees"), the note identifies that store or plugin instead: their pools hold data only, and a container renders only a pool its own component declares. Read the store's pool as this.stores.swarm.pools.bees and draw it in the component's tick(); see Pools in a Store.

WF-410 Entity spawns produce mixed shapes (hidden-class deoptimization)
Pools

Two spawn paths in one pool produced entities with different fields or a different field order. V8 gives a pool one fast hidden class only when every entity shares the same shape; once shapes diverge, every hot-loop read in the pool slows down. This is platform physics rather than a framework rule. Make all spawn paths build entities with the same fields in the same order: initialize missing fields up front (null or 0), or split differently-shaped entities into separate pools. Fields filled by entity.state defaults count as normalized and stay silent. Fires once per pool, showing both shapes. The entity.state section shows shape normalization. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-411 entity.computed pool reached a frame-budget size
Pools State

A pool that declares entity.computed properties reached 200 entities. Entity computeds are uncached by contract (they re-evaluate on every read, roughly 60us per entity per flush measured), so on a per-frame pool this cost lands on every animation frame. For per-frame pools at this scale, store the derived value as a plain data field updated on mutation, or mark non-animating entities static with data-pool-static="prop". Passive pools (data-pool-static on the container) never flush per frame and stay silent. Fires once per pool. The entity.computed section states the cost and the alternative. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-412 Named or typed template never resolved
Lists

A template lookup by name or type found nothing to render, or found something unusable: a configurable template missing from the hierarchy with no fallback, a target component that does not exist, a polymorphic item type with no matching data-type template and no default, a template with empty content, or duplicate item-template names (the first wins). The warning includes the template or type it searched for. Check the name against the defining component, or add a fallback/default template. The Template Hierarchy Lookup section states the search order. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-414 Index-dependent method or direct mutation on pool storage
Pools

Pools use swap-with-last storage, so positions reshuffle on every removal and index-based operations would hit a different entity than intended. In dev builds pool.splice(), pool.pop(), pool.indexOf(), and pool.slice() exist as throwing stubs that explain this, and mutating pool.items directly is caught by a consistency check at the next API call. Iterate pool.items freely; mutate only through the pool API (add/remove), use remove(key) to delete and at(i) for stable DOM-order access. Production keeps the raw array (pull mode's zero-overhead contract). Dev-mode only. The Array-like readers section lists what the pool exposes instead.

WF-415 Pool entity key missing or duplicate
Pools

An entity added to a keyed pool is missing the declared key property, or carries a key the pool already holds; the entity is not registered. Give every entity a unique value for the pool's key property before adding it. The data-key section states the uniqueness rule. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-416 Pool declared in a component has no container
Pools Components

A component's pools block declares a pool, but the component has no data-pool element for it by the time the page settles. A component pool is drawn into that container, so without one there is no handle and this.pools.name reads null. Add the container inside the component, with a <template> child. A pool that is never drawn belongs in a store instead: a store has no DOM, so its pools are data only. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds. It is not repeated when WF-408 has already reported a container whose name does not match.

WF-417 Pool key set two ways that disagree
Pools Components

A component pool's container has a data-key attribute, and the pool's entry in the pools block sets a different key. The pool keys its entities on the data-key value, so lookups and duplicate checks by the other property silently miss. Keep one of the two, or make them the same. The pool definition section covers both. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-418 Pool key is not a property name
Pools

A pool's entry in a pools block sets key to something other than a non-empty string, such as a number, an array or null. The key must be the entity property to key on, written as a string, so any other value is ignored: a component pool keys on its container's data-key, else id, and a store or plugin pool keys on id. Lookups by the property you meant then miss. Quote the property's name, as in key: 'uid'. The pool definition section covers the option. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-501 Error evaluating binding expression
Bindings

A data-bind expression failed to evaluate. Check for typos in property names or invalid JavaScript expressions. Also emitted as a warning when store shorthand ($store.path) is used in data-model. The Expressions page is the syntax reference.

WF-502 Error evaluating class binding
Bindings

A data-bind-class expression failed to evaluate. Check that the expression returns a valid string or object. The Style Bindings page shows the accepted shapes.

WF-503 Failed to create HTML binding context
Bindings

Creating a context for a data-bind-html binding failed. Check that the binding path is valid.

WF-504 Error updating conditional context
Bindings

Updating a data-show or data-render conditional context failed. Check that the bound expression evaluates to a boolean-like value. The Conditional Rendering page covers the expression forms.

WF-505 Class binding shape mismatch
Bindings

A data-bind-class binding received a value that is not a string. The element-level path expects a space-separated class string. Inline expressions can use the {'class-name': condition} object form, but a computed property should return the resolved string itself. The framework coerces the value (truthy keys joined to a string, or the value stringified) so the page keeps rendering, but the underlying mismatch should be fixed in your code. The Class Binding with Computed Properties section shows the resolved-string pattern.

Wrong: computed: { classes() { return { 'is-active': this.active }; } }
Right: computed: { classes() { return this.active ? 'is-active' : ''; } }

WF-507 data-prop path unresolvable in the parent
Bindings Components

A data-prop-* (or data-props) value looked like a path, resolved to undefined, and was still absent from the parent's state, computed properties, and methods after the page settled. A typo'd path is indistinguishable from a real prop at resolution time, so the check waits out the init window first; a parent that sets the key in init() stays silent, as does a prop whose declared default absorbed the miss. The warning suggests the closest matching parent name. If the parent genuinely provides the value later (for example after a fetch), the note can be ignored. The Prop Value Resolution section states the lookup order. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-508 Prop attribute names a prop the component never declared
Bindings Components

A data-prop-* attribute (or a key inside data-props) was passed to a component that never declared that prop, so the value is never read. Only declared props are consumed; this is the prop-name sibling of WF-507's path typo (data-prop-titel against a declared title, or data-prop-user-id against userId). When the component declares props, the warning suggests the closest declared name; when the component has no props block at all, every prop attribute on it is dead and the warning shows the declaration to add (props: { title: { type: String } }). Fix the attribute name or declare the prop. The Props Definition Reference shows the declaration forms. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-509 Binding validation
Bindings

A binding references a name that does not resolve: a data-bind/data-model/data-show/data-render path, an identifier inside a class or style binding expression, a nested path segment, a type hint that does not match the value, or a data-action method that does not exist on the component. The development build warns at bind time with a did-you-mean and the list of available names. On by default in development builds; disable with debug: false. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-510 data-props attribute failed to parse
Bindings

The bulk data-props attribute takes two forms, and this warning belongs to the second. The object-expression form (data-props="{ title: cardTitle }", unquoted keys, values as parent state paths) never parses as JSON and never raises this. The quoted-key form (data-props='{"title": "Dashboard"}', values as literals) must be valid JSON, and this fires when it is not, so no props were passed. Common causes are single quotes around keys and strings, or unescaped quotes inside values. For dynamic values, prefer the object-expression form or individual data-prop-* attributes with paths. The data-props section shows both forms. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-512 A data-model root names both a state key and a store
Bindings State

A data-model path's first segment matches a key the component's own state declares and the name of a registered store. One rule decides, the same rule bare names follow everywhere in the framework: the component comes first. The binding is component state in both directions, since typing writes it and repaints read it, and the same-named store is never touched. The store-backed form pattern is the fallback for roots the component does not own, so if the store is what you meant, rename the local state key or the store. This note exists because a name serving two masters reads ambiguously even when the resolution is well-defined; it is said once per component and root. The Store-Backed Inputs section covers the pattern and the rule. Warning severity: dev-mode only, stripped from production builds.

WF-513 Binding attribute has no component ancestor
Bindings

data-bind, data-show, data-model, or data-action was found with no data-component ancestor. Bindings only process inside a component, so the element never initializes; it silently keeps its placeholder markup instead of throwing. Wrap it in a component; an empty definition is enough (wildflower.component('shell', {}) around a <div data-component="shell">). Mirrors the existing data-query orphan check (WF-963) for the four core binding attributes every tier ships. Warned once per element, dev-mode only, stripped from production builds.

WF-514 Presence-only modifier carries a falsy-looking value
Bindings

A modifier that the framework reads by presence alone (data-model-lazy, data-model-number, data-model-trim, data-event-self, data-event-stop, data-event-prevent, data-event-outside, or data-external) was written with a value of "false", "0", "no", or "off". None of these call sites read the value, only whether the attribute exists, so the attribute being present turns the modifier ON regardless of what it says, which is the opposite of the author's intent. Remove the attribute entirely to turn it off, rather than setting a falsy value on it. Warned once per element, dev-mode only, stripped from production builds.

WF-515 Late registration crossed into chunked initialization
Bindings

A component was registered with wildflower.component() after the page-load scan had run, and more of its elements were already in the document than could be initialized inside the synchronous budget (the first 64 always, then up to 20 ms). The rest are being initialized in 8 ms chunks between yields, so code that reads those elements straight after the registration call may find some of them not yet initialized. Either load the registration with defer in <head>, so the page-load scan handles it without blocking, or await wildflower.whenIdle() (or listen for the wildflower:idle event) before reading them. Warned once per registration, dev-mode only, stripped from production builds.

WF-516 A list-row binding names a component method
Bindings

Bindings read state and computed properties, never methods. Inside a list row, a binding that names a method, bare (data-bind="total") or called (data-bind="fmt(n)", data-show="isBig(n)"), renders nothing. Outside lists the same mistake is WF-509. For a value per row, declare an item-level computed, computed: { total(item) { ... } }, and bind its name. Names that are state, computed properties or fields of the list's items, and words inside quoted strings, are not reported. Fires once per component and method, before the list first renders; warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-517 data-render on a list row's root element
Bindings

A list's row template has data-render on its outermost element, as in <li data-render="visible">. This is not currently supported. A list tracks each row by its outermost element, and data-render takes that element out of the document, leaving a placeholder the list does not track. The row hides, but the list can no longer show it again, remove it or reorder it reliably. To hide whole rows, put data-show on the row, or bind the list to a computed that filters the array. data-render on an element inside the row works. Fires once per component and list, before the list first renders, and covers nested lists; warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-518 A component element is also a list or pool container
Bindings

An element carries data-component together with data-list or data-pool. The framework handles that element as a component only, so the list or pool on it never shows the component's own data. Depending on where the element sits, it shows the parent component's data or nothing at all. Put the list or pool on an element inside the component. The Lists page covers list containers. Fires once per component and attribute, when the component initializes; warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

Wrong: <ul data-component="tag-list" data-list="tags">...</ul>
Right: <div data-component="tag-list"><ul data-list="tags">...</ul></div>

WF-519 A dotted list path inside a row may not read what you meant
Bindings

Inside a list row, data-list="settings.options" reads the component when its first segment, here settings, is a key of the component's state: block, a computed that takes no row argument, or props when the component declares props, and reads the row item otherwise. The warning fires in two cases, both judged on the first row. In the first, the component declares the first segment and the row item has a field of the same name, so the item's field is not read. In the second, the component does not declare it and the row item has no such field, so the list renders nothing in that row. In the first case, rename one of the two fields. In the second, declare the field in state: if every row should read the component's list. The Lists page covers the rule. Fires once per component and path; warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

Wrong: <ul data-list="settings.options"> in a row when only init() sets this.settings
Right: declare it, state: { settings: { options: [] } }

WF-520 A component's tag and its root: block set the same attribute, style property or directive
Bindings

A component's root: block and the bindings on its tag both set the same attribute, style property or directive. The tag's bindings read the scope of the markup around the component, which is the parent for a nested component, and the tag's value is the one used. Classes and show do not conflict, since a class is on while either side holds it and the root is hidden while either side hides it. Set the value in one place, on the tag for the markup around the component to control it, or in root: for the component itself. The Components page covers both sides. Fires once per component and name; warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

Wrong: <div data-component="dropdown" data-bind-attr="{ title: hint }"> with root: { attr: { title: 'label' } }
Right: keep title on one side

WF-521 A binding on a nested component's root reads a value only the nested component has
Bindings

A binding on a nested component's tag uses a name that the nested component has and the component around it does not, so it shows nothing. The bindings on a component's tag are read in the scope of the markup around it, which for a nested component is the component around it. Move the binding onto an element inside the nested component, or declare the component's own side of its root in its root: block. The Components page covers both sides. Fires once per component and attribute; warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

Wrong: <div data-component="swatch" data-bind-style="{ background: bg }"> inside another component, with bg in swatch's state
Right: root: { style: { background: 'bg' } } in swatch's definition

WF-601 Error in action handler
Actions

A data-action handler method threw an exception. Check the method referenced in your data-action attribute. The Events page covers action handlers.

WF-602 Error in component method
Actions Components

A component method execution failed. Check the method for runtime errors such as accessing undefined properties.

WF-603 Cannot emit: component instance not found
Actions Components

emit() was called but the component instance could not be located. Ensure the component is mounted and initialized. The Communication page covers emit().

WF-604 data-action targets a reserved lifecycle name
Actions

A data-action points at init, tick, destroy, or another lifecycle hook. Lifecycle names run on the framework's schedule, not on events: tick() runs every animation frame, destroy() tears the component down, and with the element still in the DOM the next scan auto-resurrects it. Rename the handler to a specific verb (increment, handleClick, refresh). The Method Naming Mistakes section covers the reserved list. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-605 Stale event API in a replayed action
Actions

An action fired before init() finished was queued and replayed afterward, and the handler then called preventDefault(), stopPropagation(), or stopImmediatePropagation() on the original event. By replay time the browser has already processed the event, so the call is a no-op. Put data-event-prevent on the element to block the default reliably; the framework intercepts before user code runs. The Early Submit Edge Case section walks the exact scenario. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-606 $entity.path in data-action
Actions Stores

$entity.path is a read accessor for external state and cannot name an action handler; actions are component-locked by design. To delegate to another entity's method, define a one-line wrapper on the component: bump() { this.getStore('name').bump(); }. The warning shows the exact wrapper for the path you wrote. The Communication page covers cross-entity calls. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-607 data-event-capture on an action in a list row
Actions Lists

An action in a list row carries data-event-capture. Actions in rows are dispatched by one listener on the list, which runs after the capture phase, so the modifier has no effect there. The other modifiers (data-event-self, -prevent, -stop, -once, -debounce, -throttle, -delay, -if) all apply to row actions. Remove data-event-capture, or put the action on an element outside the list if it has to run during capture. The same applies to an action in content re-inserted by data-render or moved by a portal. The Advanced Events page lists the modifiers. Warning severity (console.warn, never throws), once per element; dev-mode only, stripped from production builds.

WF-701 Route not found
Routing

Navigation attempted to access a route that hasn't been defined. Check your route configuration. The Basic Setup section shows route definitions.

WF-702 Target route not found for alias
Routing

A route alias points to a target route that doesn't exist. Verify the alias target matches a defined route path. The Route Config Options table documents alias.

WF-703 Error in route guard
Routing

A navigation guard function (beforeEnter, beforeLeave, etc.) threw an exception. Check the guard function for errors. The Route Guards page covers the guard contract.

WF-704 Navigation queue exceeded retry limit
Routing

The navigation queue has exceeded its maximum retry attempts. This usually indicates a redirect loop in your route guards. The Navigation Cancellation section shows the levers.

WF-705 Named route not found
Routing

Navigation by name references a route that doesn't exist. Check the name property in your route definitions. The Named Routes section shows the property.

WF-706 Invalid route configuration
Routing

A route definition has an invalid structure. Routes require at minimum a path property. The Basic Setup section shows the shape.

WF-707 Router already initialized
Routing

Attempting to initialize the router more than once. The router should only be configured and started once per application.

WF-708 No route matched for path
Routing

No route pattern matches the requested URL path. Consider adding a catch-all route (path: '*') for 404 handling. The 404 Handling section shows both approaches.

WF-709 Error in route handler
Routing

A route's handler function threw an exception during execution.

WF-710 Error loading route component
Routing

An async/lazy-loaded route component failed to load. Check the network request and module path. The Lazy Route Components section covers the feature.

WF-711 Error in scroll behavior
Routing

The scroll restoration or positioning function threw an exception after navigation. The Scroll Position Restoration section covers the surface.

WF-712 Error in route lifecycle hook
Routing

A route lifecycle hook (beforeEnter, afterEnter, etc.) threw an exception. The Route Guards page covers the hook contract.

WF-801 Error during SSR activation
SSR

Server-side rendered component activation failed. The server-rendered HTML may not match the expected component structure. The SSR Troubleshooting table maps symptoms to causes.

WF-802 Error during hydration
SSR

Hydrating server-rendered HTML encountered an error. Ensure the server-rendered markup matches the client component's expected structure. The SSR Troubleshooting table maps symptoms to causes.

WF-901 Store component name must be a string
Stores

wildflower.store() was called with a non-string first argument. The store name must be a string. The Stores page shows the registration form.

WF-902 Store component definition must be an object
Stores

wildflower.store() was called with a non-object second argument. The store definition must be a plain object. The Stores page shows the definition shape.

WF-903 Error in store init hook
Stores

A store's init() lifecycle hook threw an exception. Check the store's initialization logic. The Advanced Stores page covers the lifecycle.

WF-904 Error creating store component
Stores

The store creation process failed. Check the store definition for structural errors.

WF-905 Error in external() accessing store
Stores Components

external() failed when accessing a store. Verify the store name and property path are correct. The Communication page covers external().

WF-906 Error in store subscription callback
Stores

A store subscription callback threw an exception. Check the function passed to the store's subscribe() method. The Communication page covers store subscriptions.

WF-907 Failed to create default app-store
Stores

Automatic creation of the default application store failed. This is an internal initialization error.

WF-908 Store path written from inside its own notification
Stores

An onStoreUpdate handler wrote the store path it was being notified for, which would loop forever. The engine drops the nested notification and keeps the write, so the cycle cannot hang the page; the warning identifies the store and path. Derive the value with a computed instead, or write a different path. The onStoreUpdate section teaches the hook's correct use. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-909 Subscribed or watched store never registered
Stores

A component subscribes to, watches, or path-subscribes a store name that nothing ever registered. The miss is reported (error severity for subscribe:, warning for watchers) and init continues best-effort so the rest of the component works. Fix the name, or register the store before the component initializes. Dev-mode only, stripped from production builds. The subscribe section shows the declaration whose names are checked.

WF-910 Timed out waiting for a subscribed store
Stores

A subscribed store exists but did not become ready within the wait window (default 5000ms; per-component override via subscribeTimeout in the definition). The component's onError hook receives the timeout and init continues best-effort. Check the store's async init() for work that never resolves. The subscribe section covers the readiness wait. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-911 A store with storageKey also declares pools
Stores Pools

storageKey saves and restores a store's state. Pool entities are not state: they are plain objects that nothing tracks, which is what keeps pools fast, so autoSave cannot see them change and they are neither saved nor restored. Copy what must survive a reload into state before it is saved, and refill the pool from that state in init(). The Pools in a Store section covers store pools. Warning severity (console.warn, never throws); fires once at registration, dev-mode only, stripped from production builds.

WF-940 A named operation did not inherit the query-level confirmation
Queries

A query-level confirmation: reaches update and create only. Every other operation named in to: declares its own or has none, the same rule body: follows. The write still succeeds. What is lost is the reconciliation: with no confirmation the response is treated as transport, so the query invalidates and refetches, and that refetch clears the stored ETag first, which makes it a full collection body on every write of this shape. The endpoint that answers a named operation with the updated record is the common case, so the response usually carried exactly what the row needed. Declare confirmation on the operation itself to reconcile from its response, or leave it alone if the refetch is what you want. delete never fires this, since 204 No Content is the ordinary answer and carries nothing to confirm. Named operations covers the per-operation declarations. Warning severity: dev-mode only, stripped from production builds; the refetch happens in every build. Fires once per query.

WF-941 data-query is on the <template> element itself
Queries

The attribute belongs on the container element, with <template> as its child, not on the <template> tag directly. A template's own light-DOM children are always empty (its real content lives in .content), so this reads as a valid empty record-shape query and silently binds nothing. Mirrors WF-401 ("no <template> found"), which data-list and data-pool already hit in this same shape; data-query needed its own check because "no template child" is also its normal record-shape signal. Move the attribute to the parent: <div data-query="name"><template>...</template></div>. Warned once per element, dev-mode only, stripped from production builds.

WF-942 A query config key that queries do not read
Queries

The key was ignored. Usually a typo (refersh for refresh), which the note corrects, or a key from another entity kind. A query takes no pools, state or computed: the engine authors its shape, and its collection is rows. To hold a query's rows in a pool, copy them into a store's pool from a watcher. The note lists every key a query reads. Warned once per key at registration, dev-mode only, stripped from production builds.

WF-950 External write to a query-owned store
Queries

A field on a query's backing store (rows, isLoading, isStale, error, syncError, lastSync) was assigned from application code. Query stores are engine-owned: the fetch pipeline rewrites them on every sync, so an application write survives only until the next refresh replaces it. The supported pattern is to mutate the data source, then call wildflower.getQuery('name').invalidate(). The write still lands, since a deliberate optimistic update is legitimate; the warning fires once per store. The Writes & Optimistic Updates page teaches patch(), the sanctioned form of this write. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-951 Query name already registered
Queries

A second wildflower.query() call used a name that is already registered. The second registration is ignored and the existing query's handle is returned, so config changes in the second call never apply. Declare each query once, at module scope. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-952 Query name collides with an existing store
Queries

Queries and stores share one entity namespace, because every query is backed by a store of the same name. A wildflower.query() call whose name matches an existing store is refused (null is returned) rather than silently taking over the store's data. Pick a name no store uses. The Entity Model page explains the shared namespace. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-953 Query from is not a URL or function
Queries

The from option must be a URL string or a function returning the data (or a Promise of it). Registration is refused (null is returned). There are no other source types: anything beyond a URL is expressed as a function. The Sources and Refinement page covers both forms. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-954 Sub-second poll rung
Queries

A numeric refresh rung below 1 is almost always a units mistake: poll values are seconds, so 0.5 means twice per second (120 requests per minute against the source), where the author usually meant "every 30 seconds". The rung still runs as declared. If sub-second polling is genuinely intended, the warning can be ignored. The Refresh Ladder section defines the rung values. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-955 No such query is registered
Queries

Fires in two shapes that share this code: a data-query attribute in markup names a query that was never declared (the element is left untransformed and never activates), or getQuery('name') is called for an unregistered name (returns undefined). Register the query with wildflower.query('name', { from: ... }) before the markup mounts, and check for typos between the attribute and the declaration. The Data Queries page shows the declaration surface. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-956 data-seed is not valid JSON
Queries

A data-seed attribute on an adopted row or query element could not be parsed as a JSON object, so its fields were ignored; the row keeps only what the display text parse recovered. Common causes are single quotes inside the JSON, unquoted keys, or a non-object value (arrays are ignored by design). The data-seed section states the seed contract. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-957 The sse rung needs a stream URL
Queries

The 'sse' rung opens an EventSource, which requires a URL. When from is a URL string it doubles as the stream endpoint, but when from is a function there is nothing to connect to, so the rung is skipped (other declared rungs still run). Add a stream: option naming the SSE endpoint. The Server-Sent Events section shows the declaration. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-958 SSE message was not valid JSON
Queries

The stream contract: a JSON message body is applied as the new result, an empty message is an invalidation signal (conditional refetch). A non-empty message that fails to parse as JSON fits neither, so the engine degrades it to an invalidation and refetches, keeping data correct at the cost of one extra request. Fix the server to send JSON bodies or empty invalidation pings. Named once per query per page load; later non-JSON messages still invalidate, silently. The Server-Sent Events section states the two-message contract. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-959 Record query resolved null
Queries

A record-shaped query's source resolved to null or undefined. This is a valid empty context by design: bound fields render empty and nothing throws, matching "no result yet" states like a logged-out session. The warning exists because a permanently null record often means the source returns a wrapper shape ({ data: {...} }) rather than the record itself. Named once per query per page load, across both delivery paths (fetch and stream). The Record Semantics section covers the null-record contract. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-960 Append received rows without the declared key
Queries

An append refresh (refresh({ params, append: true })) received rows that lack the query's declared key field. Accumulation identifies rows by key to dedupe and merge, so keyless rows would duplicate endlessly; the engine applies the result as a plain replace instead and warns. Give appended rows the declared key, or declare the key the source actually returns. The append section states the key requirement. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-961 refresh() received unexpected options
Queries

refresh() takes an options object with three recognized fields, params, append, and clear. Any other key is almost always request parameters passed directly (refresh({ status: 'open' })), which the engine cannot distinguish from an option name and therefore never sends. Nest them: refresh({ params: { status: 'open' } }). Keeping parameters inside params is what guarantees no request parameter can ever collide with an option name. The refresh still runs, without the stray values. The URL Sources section shows the nesting. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-962 Incoming rows drifted from the data-expect declaration
Queries

Incoming rows broke an element's data-expect="field:type, …" declaration, either a declared field missing from the rows or one present with a different primitive type. The context gives the query, the field, and which source carried the drift (fetch, stream, ssr, or patch). A null value counts as data rather than drift, and a token with no type checks presence alone. Each distinct drift warns once per query, so polling never floods the console. This is a tripwire rather than a validator: nothing is coerced, transformed, or rejected, and rendering proceeds unchanged. For real validation, wrap the function source with a validator. Declaring the Expected Shape teaches the declaration. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-963 data-query element has no component ancestor
Queries

A [data-query] element sits outside any component, so nothing will ever process it. The query transform runs during component binding, so an element with no data-component ancestor is skipped entirely, with no fetch, rows, or error state, leaving markup that looks correct and does nothing. Wrap it in a component; an empty definition is enough, as in wildflower.component('shell', {}) with <div data-component="shell"> around the query element. Detected by a post-scan sweep and warned once per element. Where It Runs states the rule. Warning severity (console.warn, never throws); dev-mode only, stripped from production builds.

WF-964 write() needs a to function declared on the query
Queries

write() was called on a query that never declared to, so there is no transport to hand the item to and the call rejects rather than applying an optimistic update that could never sync. A query describes its resource in both directions: from for how rows come in, to for how a write goes out. Declare it on the query, as in to: item => fetch('/api/orders', { method: 'POST', body: JSON.stringify(item) }). Development builds also warn at registration when to is declared in a shape that cannot route (WF-971). Declaring the Destination shows all three forms. Error severity (the promise rejects, so .catch and an awaited try/catch both see it); present in all builds.

WF-965 write() resolved with something that is not the row's record
Queries

Two shapes reach this, and both fall back the same way: a keyed write resolving with an object that has no key field (usually a status envelope like { success: true }), or a resolution that is not an object at all, such as confirmation: r => r.ok. Neither can be applied as the row's record, so the engine treats the resolution as nothing and invalidates; the next conditional fetch carries the truth. Record queries see only the second half, since their writes are keyless and a keyless object is simply the record. To keep that fallback without the warning, resolve with nothing. To apply the response directly, echo the key onto it: .then(r => r.json().then(j => ({ ...j, id: item.id }))). Telling the query what happened covers confirmations. Warning severity (console.warn, never throws); dev-mode only, and the invalidate fallback applies in all builds.

WF-966 A container written to state holds a facade-wrapped object three or more levels deep
State

A value written to reactive state contains a reactive facade nested three or more levels inside it. Reading state hands back facades, so a copy of state carries them along: this.items = this.items.filter(...), [...this.items, item], and this.items.map(i => ({ ...i, done: true })) all do. The engine unwraps the value you assign and the first two levels inside it on every write, in every build, so those forms store plain objects and work as they do elsewhere. A facade deeper than that reaches the raw state graph, each later read wraps it again, update routing degrades, and every read of the container walks a growing proxy chain. Build the deeper copy from unwrapped data, as in wildflower.toRaw(this.items), or copy only the level you are changing. Detected with a bounded scan and warned once per stored container. State covers wildflower.toRaw. Warning severity (console.warn, never throws); dev-mode only, and present in every tier's development bundle since any component with reactive state can reach it.

WF-967 Unrecognized refresh rung token
Queries

A token in a refresh declaration matches no rung, so the query runs without the rung you meant to declare. The valid tokens are a bare number for the poll interval in seconds, 'focus', 'reconnect', 'sse', 'etag:N', 'fresh:N', and 'once'. The usual cause is inventing a prefix for the poll interval: 'poll:15' is not a token, and the poll rung is the bare number, refresh: [15]. The Refresh Ladder lists every valid token. Warning severity (console.warn, never throws); dev-mode only, and the token is ignored in all builds.

WF-968 Query persist must be true or a storage-key string
Queries

The persist option is neither true nor a non-empty string, so persistence is off for this query, which otherwise runs normally. persist: true stores the last confirmed server rows under a derived key (wf:query:<name>), and persist: 'my-key' chooses the key directly, which is how per-user keys are done. On the next load a query with stored rows paints them immediately, marks itself isStale, and revalidates. Persistence covers the whole surface. Warning severity (console.warn, never throws); dev-mode only, and an invalid option is ignored in all builds.

WF-969 write() was called without the query's key field
Queries

An item passed to write() with no value for the key identifies no row, so the write is rejected before anything happens: nothing applies optimistically and no field claims are taken. Include the key alongside the fields being changed, as in write({ id: 7, done: true }), or use create() to add a row instead of changing one. The query's shape decides this, not whether you spelled out key:, since key defaults to id and a query rendering a list is keyed either way. Record queries hold one record and accept unkeyed payloads; a query bound as both counts as a list, because a list binding means its rows carry keys. Writes teaches the rule. Error severity (the promise rejects, so try/catch and syncError both see it); the rejection happens in every build, while the diagnostic text is dev-mode only.

WF-970 The query params function threw
Queries

A params function threw, so the request departed carrying only the values passed explicitly to refresh({ params }). The usual cause is reading through something not there yet, such as a route store that has not populated or a nested property on an object that is still null. Guard the read or supply a default. This matters more than it looks because the function resolves once per request, including internal refetches from a settling write, a poll tick, a focus or reconnect, and a missed-arrival catch-up. Named once per query per page load. URL Sources shows the params-function form. Warning severity (console.warn, never throws); dev-mode only, and the fetch proceeds identically in all builds.

WF-971 A to or create declaration is not a shape the query can route
Queries

A to or create declaration is not a shape the engine can route. The valid forms are a URL string (to: '/api/items/:id', becoming update at PATCH and delete at DELETE), a map of named operations, or a function. This fires on an entry with no url, a body or confirmation that is not a function, an array, or create declared inside the to map. create is its own top-level key because it alone mints identity: it is called as create(item), its URL must not carry the key token, and a rejection removes a row that only ever existed optimistically. The malformed operation is dropped, so calling it later raises WF-974. Declaring the Destination shows every routable shape. Warning severity (console.warn); dev-mode only, though the operation is unavailable in every build.

WF-972 A :token in the request URL had no value, so nothing was sent
Queries

On a read this is a wait rather than a failure. Nothing is recorded, rows on screen stay put, and the query fetches when the value lands, so an unresolved route parameter is the usual and harmless cause. Dev builds name the wait once per token, which is how a misspelled token still surfaces. On a write the promise rejects and identifies the token, because a write runs only when the application calls it. Carry the value on the item being written, or supply it from params. URL Templates covers token resolution. Error severity on the write path and no error at all on the read path; the write rejects in every build, while the diagnostic text is dev-mode only.

WF-973 A :token in a declared URL names nothing the query can supply
Queries

The declaration-time half of WF-972, raised at registration instead of at the first request, in two cases. A token in from that a static params object does not supply is almost always a typo. (Only from is checked, since to URLs interpolate the item's own fields and a parent id on the row is legitimate.) The other is a create URL interpolating the key token, which resolves only when create() is passed a key you made, as in the client-generated-id pattern against a PUT endpoint. Where the server issues the key instead, point create at the collection URL; a create reaching the transport with a framework-minted key and such a URL is refused in every build, since that address names a temp id the server never issued. URL Templates covers token resolution. Warning severity (console.warn): dev-mode only.

WF-974 write() or create() named an operation the query does not declare
Queries

A map declares exactly the operations it lists, so one it omits is unavailable rather than approximated, and an undeclared name never falls back to update. The message states the operation asked for and lists the declared ones. The usual causes are a spelling difference between declaration and call site, a create() on a query with no create key, or a delete on a query that declares deleted but no delete route (which also warns at registration, WF-975). Naming an operation on a query whose to is a function raises this too, since a function receives the item and nothing else and carries no operation hint. Named operations shows the map form. Error severity (the promise rejects, nothing applies optimistically, no request departs); raised in every build.

WF-975 The delete operation and the declared deleted field disagree
Queries

The declared deleted field and the declared delete route disagree, in one of two directions. Declaring deleted: 'removed' beside a map with no delete entry warns at registration and errors at the call site, rather than being refused outright, since a resource may carry a server-driven tombstone field for arriving rows while the client never issues deletes. The reverse is write('delete', item) on a query declaring no deleted field: the request departs, but nothing marks the row deleted on screen, so it stays visible until the next sync. A named delete always applies the declared tombstone itself, so one map entry cannot behave differently by derivation than by name. Deletes teaches both halves. Warning severity at registration; the call-site failure is WF-974.

WF-976 The written item merges no fields, so nothing can roll back
Queries

An item carrying only the key, as in write('favorite', { id: 7 }), claims no fields, so its rejection rolls nothing back and later writes to the same row have nothing to order against. The write machinery works over a row key and a field: it applies an optimistic value, claims the field, and reverts the fields a write still owns when it fails. Include the fields the operation changes, as in write('favorite', { id: 7, favorited: true }). The request is unaffected and still departs, so this is correct behavior that reads as a bug, named once per operation. Named operations shows the field rule. Warning severity (console.warn): dev-mode only.

WF-977 An update or create carries fields but declares no body
Queries

An update or create is about to depart with no body while the item passed to it carries fields, which is a request that succeeds and changes nothing. An operation sends no body unless one is declared, because payloads differ within a single query: a body-less DELETE beside an envelope-carrying POST is ordinary, and guessing for one would send the wrong thing for the other. The query-level body covers update and create, and every other operation declares its own on its entry. Declare the shape with body: item => item for a plain API, or body: item => ({ article: item }) for one expecting an envelope. Warned once per query. Declaring the Destination covers body. Warning severity: dev-mode only.

WF-978 A declarative option cannot be honored beside a function from
Queries

A function from is called with no arguments and builds and parses its own request, so params and select have nothing to act on for reads and the function must derive its own values and return its own rows. Declaring them anyway states an intent the framework cannot honor. There is one exception: params still feeds :token segments in write URLs, so a query keeping a function from while declaring its writes is a supported shape, and no warning is raised when a declared operation URL carries a token. Function Sources states which options a function form honors. Warning severity (console.warn) at registration; dev-mode only.

WF-979 A refetch dropped values the last refresh({ params }) had applied
Queries

Values passed to refresh({ params }) apply to that one fetch. The next refetch the engine starts on its own resolves the declaration again and reasserts its own values, which on screen reads as a value spontaneously reverting, since the refetch was a poll tick, a stream invalidation, a focus refresh, or the revalidation after a write settled. The warning fires at that refetch, reports what was dropped, and fires once per override. A query that never refetches on its own cannot reach it, an explicit refresh() clears the override quietly, and an override passed with append: true never arms it. When a value should persist, move it into whatever params derives from, such as a store field the params function reads, and keep refresh({ params }) for genuinely one-off requests. Paged and Infinite Results shows the rewrite for a paged query beside a rung. Warning severity: dev-mode only.

WF-980 The read response did not arrive as an array of rows
Queries

A list-shaped query expects an array, and a response that is not one becomes a single row whose fields are that object's. An API answering { articles: [...], articlesCount: 47 } therefore renders one row with the fields articles and articlesCount. Name the array with select: d => d.articles. The companion case is a response, or a select, resolving null or undefined, which empties the list; the message says which happened, since "the server sent nothing" and "the transform missed" look identical on screen. Record-shaped queries never raise this, since a single object is their answer. Named once per query per page load. Shaping the Response teaches select. Warning severity: dev-mode only.

WF-981 A headers declaration is not an object or a function, or it threw
Queries

This fires when a headers declaration resolves to something that is not an object, when a headers function throws, or when a config({ headers }) key is not an origin. The request still departs, without those headers. Headers come from two places: config({ headers }) sets a default scoped by origin, and a query's own headers overrides it key by key. Either may be an object or a synchronous function, and the function runs once per request attempt, retries included, so a refreshed token is the one actually sent. Named once per query per page load, with the config-key check suppressed once per config object. Headers and Credentials covers both forms and why the keys are origins. Warning severity: dev-mode only.

WF-982 create() was called on a record-shaped query
Queries

A query bound without a <template> is record-shaped, and its subtree binds one record. create() makes a new row, which a binding that renders one record has nowhere to show, so the call is rejected rather than appending something invisible. Use write(item) to change the record you have. If the query really holds many rows, bind it as a list with a <template> child and create() works as documented. A query bound both ways counts as a list, so a record view sitting beside a list view of the same query never raises this. Writes covers the shape rules. Error severity (the promise rejects); the rejection happens in every build, while the diagnostic text is dev-mode only.

WF-983 Query retry must be a number
Queries

The retry option is a plain attempt count, as in retry: 3, clamped to 0-10 with a fixed doubling backoff, and it takes no policy object. Any other shape coerces numerically, and most coerce to 0, which turns retry off entirely; retry: { max: 3 }, the shape several data libraries use, declares no retries at all. That is otherwise invisible, since a retry ladder only shows itself when a request fails. The warning reports the shape at registration and states the value it actually coerced to. Automatic Retry states the whole surface. Warning severity (console.warn, never throws); dev-mode only, and the coerced value applies in all builds either way.

WF-984 Declared headers cannot apply to the sse stream
Queries

A query with declared headers opened an sse stream, which cannot carry them. The limit is the platform's: the browser's EventSource constructor accepts no headers, so the stream connects anonymously even though every fetch on the same query authenticates. A server requiring the header rejects the stream, and on a query that has never synced that rejection is easy to miss, since the connection retries quietly in the background. Named once, at the moment the stream opens. Authenticate the stream another way, such as a token in the stream URL's query string or cookies on a same-origin stream. Headers and Credentials covers where declared headers do apply. Warning severity: dev-mode only, stripped from production builds.

WF-985 Query initial must be an array of rows
Queries

The initial option seeds rows to paint before the first fetch answers, and it takes an array in every shape. A record-shaped query is the trap, since its natural seed reads as a single object while the seed is still a one-element array, as in initial: [{ name: 'Loading…' }]. Any other shape is ignored, so the seed never arrives and the loading flash this option exists to remove appears anyway. The warning appears at registration. Query Shapes shows the record seed. Warning severity: dev-mode only, and a non-array seed is ignored in all builds.

WF-986 Declared headers rode a redirect to another origin
Queries

A response revealed that a request carrying declared headers was redirected to another origin. On such a hop the platform removes Authorization and forwards every other header, so an API key or tenant header declared for the first origin arrives at the second. No framework code runs between the two requests, so the hop can be reported but not intercepted. Named once per query, on the first response that reveals it, for reads and writes alike; a same-origin redirect never warns. If the redirect is expected, point from or to at the final URL so the request starts where it ends. If it is not, this is the earliest visible sign that credentials are leaving the origin they were declared for. Headers and Credentials covers the scope guarantee. Warning severity: dev-mode only, stripped from production builds.

WF-987 write() received an undefined-valued field
Queries

A field passed to write() as undefined is treated as absent, since undefined is not a value the wire can carry. JSON serialization already drops it from the request body, so letting it through on the client would write undefined over the row's real value while the server never hears about the field. The rule matches the confirmation format: null clears a field, an absent field is left alone, and undefined means absent everywhere, so it is not merged, claimed, or sent. Pass null to clear a field, or omit it to leave it alone. The dropped fields are named once per query. Warning severity: dev-mode only, stripped from production builds.

WF-988 A persist save landed inside the clearPersisted() window
Queries

A query delivered data shortly after clearPersisted() ran, and the save that would normally follow was suppressed, because a clear holds through a short transition window. Without the window, a response already in flight at sign-out would write the snapshot right back. This is expected during a logout transition and needs no action; persistence resumes on its own once the window lapses. For full soft-logout safety on a page that never navigates, unbind the query's elements before clearing so nothing keeps syncing on the signed-out user's behalf. The Persistence section covers the sign-out story. Warning severity: dev-mode only, stripped from production builds.

WF-989 The sse rung is connecting to a JSON endpoint
Queries

With stream: absent, the sse rung connects to the query's from URL, which an ordinary JSON API does not serve as an event stream. EventSource requires Content-Type: text/event-stream and fails the connection permanently on anything else, with no reconnection, which is the most common first-contact failure with server-sent events. The framework cannot read the MIME type, so it reads the pattern: a stream permanently refused more than once, having never delivered a message, on the URL the reads use. One failed connection stays silent, since it could be an outage, and a stream that has ever delivered a message never triggers this. Point stream: at a real SSE endpoint, or drop 'sse' from refresh: if the source does not push events. Streaming covers the rung. Warning severity: dev-mode only, stripped from production builds.

WF-990 A write() rejection reached no handler
Queries

A rejection from write() or create() reached no handler. Rollback and syncError are automatic, so a fire-and-forget call looks complete, and without a handler the only trace is the browser's uncaught-rejection report, whose stack points into the framework rather than at your call. This warning gives the query, the operation, and the fact that the rollback already ran. It changes nothing: no handler is attached on your behalf, the rejection still propagates, and an awaited write in try/catch still sees the failure. Attach .catch() or await the write in try/catch in the same turn as the call, and acknowledge an intentional fire-and-forget with .catch(() => {}). Writes states the promise contract. Warning severity: dev-mode only, stripped from production builds.

WF-991 A write has been pending far longer than a request should take
Queries

A write that never settles holds its field claims, keeps pendingWrites raised, and makes persistence, validator caching, and engine refetches wait for a drain that never comes, leaving the query permanently stale. The notice fires once per query when a write passes ten seconds unsettled. Nothing is aborted and no timeout is imposed, because the transport belongs to the application and only it knows whether the request is still alive. Make the transport settle both ways, for example with AbortSignal.timeout inside a function to:. Note that writes never retry on their own, so an explicit second write() is the only retry. Writes covers the settle discipline. Warning severity: dev-mode only, stripped from production builds.

WF-992 A persisted row carries a value JSON cannot round-trip
Queries

Persistence serializes rows with JSON.stringify, which stores a Date as a string that restores as a string, and a Map or Set as an empty object with its contents gone. The live session is unaffected, so the retyping appears only after a reload. Rows parsed from resp.json() are plain JSON already; the shape that hits this is a function from reading IndexedDB or an in-memory model whose rows carry live objects. The warning identifies the first offending field once per query, and the save still proceeds, since dropping it would trade a visible warning for invisible data loss. Shape rows to plain JSON in from: or select:, using numbers or ISO strings for timestamps and arrays instead of Maps and Sets, or drop persist: for the query. Persistence covers what is stored. Warning severity: dev-mode only, stripped from production builds.

WF-993 A headers origin key differs from a request's origin only in scheme or port
Queries

Header defaults are keyed by origin, and origins compare exactly on scheme, host, and port, so a key spelled slightly wrong matches nothing and the credential never ships. The request is still legal and the server simply never sees the header, which is what makes the mistake hard to spot. The usual shapes are an http:// key beside https:// queries, an explicit :443 that request URLs never spell, or a local API on a different port than the key names. The warning fires at the first request whose origin misses the map while matching a declared key's host, naming both sides, once per key. Spell the key as the exact origin the requests use, or 'self' for the document's own origin. Headers and Credentials covers the scoping. Warning severity: dev-mode only, stripped from production builds.

WF-994 A persist save failed and was dropped, along with the stored snapshot
Queries

A persist save threw, from storage quota or from a row JSON.stringify cannot encode. The engine drops the save and removes the snapshot already on disk, because the stored rows no longer match the last confirmed truth and restoring them tomorrow would misrepresent this session. The next reload starts cold and revalidates identically. Without this warning the session reads as though persistence works while nothing is being stored, so the first drop is named once per query with the error that caused it. For quota, persist fewer or smaller queries, since rows serialize in full. For serialization, find the value JSON cannot carry; WF-992 lists the common ones before they reach this point. Persistence covers the storage stance. Warning severity: dev-mode only, stripped from production builds.

WF-995 write() received a second argument it does not take
Queries

A second argument to write(item, …) was ignored, since that form takes only the item. The two forms are write(item), which derives the operation, and write("name", item), which names one. There is no per-call options argument: the optimistic flags, success callbacks, and cache directives other libraries pass at the call site are declarations here, and to:, body:, and confirmation: decide reconciliation for every call. If a named operation was intended, the string goes first. Named once per query. Declarative Writes covers where each declaration lives. Warning severity: dev-mode only, stripped from production builds.

WF-996 write() was called with no item
Queries

A write carrying no item claims no fields, applies nothing optimistically, and can roll nothing back. A keyed list rejects such a call outright (WF-969), but a record query legitimately writes keyless items, so write("favorite") with no item flows through and the request departs carrying nothing. That can be intended, since a pure side-effect operation whose URL resolves from params: has nothing to put in an item. More often the item was forgotten, and this warning reports it directly, where otherwise a later warning about fields or the body would misdescribe the mistake. The warning fires once per query per operation and changes nothing about the write. Pass the row being written, or at least its key. Named Operations shows the call shapes. Warning severity: dev-mode only, stripped from production builds.

WF-997 A record-shape binding names a field nothing provides
Queries

Inside a record-shape query (a data-query element with no <template>), bindings resolve against the single row merged over component state, the same way a data-list template resolves against its item. A name that appears in neither renders empty, which looks identical to a field the server has not sent yet. This fires once per element, and only after a row has actually arrived, so a query still loading is never mistaken for a misspelling. Check the name against the row your endpoint returns; a field the server sends only sometimes wants a fallback in the expression rather than a bare reference. Member names, object-literal keys, globals, and $store.path references are all excluded, so this does not fire on correct code. Record Semantics covers what a record subtree can see. Warning severity: dev-mode only, stripped from production builds.

WF-998 A confirmation: callback threw
Queries

The server answered ok, but the query's confirmation: callback threw while shaping the record, so the write rejected and rolled back anyway. From the outside that is indistinguishable from a server rejection, which is why this warning identifies the function that raised and the operation it was confirming. The classic cause is confirmation: r => r.json(): the callback receives the parsed response body, not the Response, and the parsed body has no .json(). Return the record to apply (confirmation: body => body, or extract it from an envelope with body => body.item), or declare no confirmation at all for transport-only writes, where an ok response triggers one conditional refetch instead. The rejection still propagates to write()'s promise and syncError exactly as it would without the warning. Warning severity: dev-mode only, stripped from production builds.

WF-999 A write succeeded but its response body is not JSON
Queries

The server answered ok to a write, and confirmation: is declared, but the response body could not be parsed, so there was nothing to hand the callback and the query refetched instead. The write itself stands: the status said the server accepted it, and an unreadable body is not a refusal. An empty body never reaches this warning. 204 No Content is the ordinary answer to a delete, and an empty body simply means there is nothing to apply, which is silent and expected. This fires for a body that is present and not JSON, which is usually a content-type mistake or an HTML error page returned with a 200. Have the endpoint answer with JSON or with no content at all, or drop confirmation: from the operation if its body is never useful. What comes back covers the three things an ok response can mean. Warning severity: dev-mode only, stripped from production builds; the write stands and the query refetches in every build.

WF-CSP-SYNTAX Cannot parse expression
CSP Bindings

The CSP-safe expression parser encountered syntax it cannot parse. Simplify your binding expression or check for syntax errors. The CSP Mode Limitations section lists what parses.

WF-CSP-UNSUPPORTED Expression uses unsupported syntax
CSP Bindings

The expression contains a syntax construct not supported by the CSP-safe expression parser. The parser supports literals, identifiers, member access, binary/logical/unary/conditional expressions, array expressions, and function calls. Anything else (arrow functions, template literals, object literals, destructuring, assignment, etc.) triggers this error. Simplify the expression or move the logic into a computed property or component method. The CSP Mode Limitations section lists the unsupported constructs and the computed-property fix.

WF-CSP-SECURITY Blocked access to restricted API
CSP

CSP security policy blocked one of: (1) a blocked global identifier (window, document, eval, Function, fetch, setTimeout, and others); (2) a blocked property access (__proto__, prototype, constructor); or (3) a function call other than external(). In CSP mode, only external() is permitted as a function call in binding expressions. The What CSP Mode Supports section states the rule.

WF-SEC-BLOCKED Dangerous attribute or URL value blocked
Security

A binding tried to write a value the security layer refuses by design: a javascript: or vbscript: URL, a scriptable data: URI in a URL attribute (raster image formats are allowed), or a blacklisted attribute such as inline event handlers. The write is dropped in every build; the warning fires in dev builds. This is a policy outcome, not an error. If you control the value, use a safe scheme; if the value is user-supplied, the block is doing its job. The Attribute Bindings page's security section lists the blocked set.

WF-SEC-SANITIZER HTML rendered without a configured sanitizer
Security

A router outlet is rendering HTML with no sanitizer configured. If the HTML can ever include user-supplied content, configure one with wildflower.setHtmlSanitizer() (for example DOMPurify) to prevent XSS. For fully static, author-controlled HTML the notice can be ignored. The HTML Content Binding section shows the sanitizer setup. Warning severity (console.warn, never throws).

WF-EFFECT Error resolving path in render effect
State Bindings

The render effect system failed to resolve a binding path during a reactive update. The bound property may not exist on the component's state.

TH-101 Value cannot cross to the worker
Threads

A write to the thread, or an argument to one of its methods, holds something structured clone cannot copy: a function or a DOM node. A class instance is not one of these; it crosses as a plain object. The write throws the browser's own DataCloneError and the mirror is not updated; a method call rejects with it. Only plain objects, arrays, primitives and typed arrays cross. Keep the value on the page, or send the plain data it was built from. The What crosses, and when section lists what clone accepts. The throw happens on every build; the warning with the field path and the type is development-only.

TH-102 Page-side write to a field the worker owns
Threads

The page wrote to a computed property or one of isLoading, pending and error on a thread store. The worker owns those and would overwrite the value on its next patch, so the write is dropped. Write to the state the computed reads, or call a method on the thread. Dropped on every build; the warning is development-only (console.warn, never throws).

TH-103 Worker failed to start, or threw outside a message
Threads

The Worker fired its error event. Before the first patch this means it did not start: the extension or definition URL was unreachable, the framework file did not load, or a Content Security Policy refused the worker. The inline route needs worker-src to allow blob:, and so does the default form when the extension is loaded from another origin, such as a CDN, because the worker then starts from a blob: URL that loads it. Under a stricter policy, load the definition file by URL and serve the extension from the page's own origin. The same code is thrown from thread() when the browser refuses to create the worker at all. After the first patch it is an uncaught error in the worker, and the thread keeps running. The store's error holds the failure's message on every build, and calls in flight reject with an Error carrying code TH-103 when the worker did not start. The console message with the directive is development-only.

TH-104 Canceled: the thread was terminated
Threads

The thread was ended by wildflower.unregister() or wildflower.destroy() while calls or writes were in flight. Each pending promise, including a pending settled(), rejects with an error whose name is AbortError and whose code is TH-104, the shape a canceled fetch rejects with. A call made after that rejects the same way. Catch it where you catch a canceled fetch. Every build; no console output.

TH-105 Definition not usable as written
Threads

Two severities on one rule: it warns when the thread can still run, and throws when it cannot. Warned (console.warn, development-only) for a definition that is usable but wrong: a methods: block, which the store shape does not use, so those functions are not callable; storageKey or autoSave, which do nothing in a worker, since it has no localStorage; a state key, computed or method using a name the mirror reserves (name, isLoading, error, pending, snapshot, subscribe, settled, terminate, the three EventTarget methods); or a workerOnly entry that is neither a state key nor a computed. Thrown (every build) when there is no thread to create: the definition is not an object at all, or the inline route was handed state it cannot write as source, which is a cycle, a value shared between two fields, a class instance, or a function whose own name cannot be matched to the key it sits under. Declare methods at the top level beside state and computed, rename the field, fix the list, or for the inline cases load the definition by URL instead. The definition section shows the shape.

TH-106 No URL to spawn the worker from
Threads

The extension spawns its own file as the worker and loads the definition file into it, so it needs both URLs. It reads them from the running script tags. When the extension was loaded some other way (a module import, a bundler), pass { url } with its address; when thread() is called somewhere other than a definition file's top level, pass { def } with that file's URL, or use { inline: true }. thread() throws an Error with code TH-106 on every build; the message spells out the way out on the development build.

TH-107 No framework file for the worker to load
Threads

The worker runs the definition as a real store, so it loads the framework first, the same file the page loaded. The extension finds that file among the page's script tags by name. When the page loaded the framework another way, pass { core } with the URL of a classic (non-module) build, any tier. thread() throws an Error with code TH-107 on every build; the development build's message says so in full.

TH-108 Framework file loaded with no framework on the page
Threads

threads.wf.js ran before the framework did, so there was no instance to attach wildflower.thread to, and definition files that call it will fail. Order the tags: the framework, then the extension, then the definition files, all with defer so the order holds. Warning severity (console.warn, never throws); development-only.

TH-109 Worker-only array changed in place
Threads

A method in the worker mutated a workerOnly array in place (push, splice, an index write). Worker-only state is held as a plain array and is reactive by identity only, so the computeds that read it did not recompute and the page saw no change. Replace it instead: this.rows = this.rows.concat(row), or a new array from a filter or a map. Printed in the worker's console, once per field. Warning severity (console.warn, never throws); development-only. The definition section states the rule.

TH-110 The worker has not answered for five seconds
Threads

A write or a call has been outstanding for five seconds, so the page is showing values the worker has stopped updating. Two causes. A method that genuinely takes this long, a large fetch or a long computation, in which case the message is a note and there is only one of it. Or the worker is not reading its queue: a tick() that never returns, or a loop inside a method. The store's pending count says how many messages are waiting. Warned once per stall, and again only after the worker has caught up. Warning severity (console.warn, never throws); development-only.

TH-111 That name is already a registered store
Threads

A second wildflower.thread() call used a name that is already registered. No thread is created and the existing store is returned, the same rule wildflower.store() follows for a duplicate name. The check happens before the worker is spawned, so nothing is left running. Reached most often by a definition file that runs twice, which a hot reload or a second script tag will do. Call wildflower.unregister(name) first to replace a thread, which terminates its worker, or give the new one a different name. Warning severity (console.warn, never throws); development-only.

TH-112 The definition declares pools on a build without them
Threads

A thread's pools block needs a framework build that includes pools. The worker loads the same framework file as the page, so on a build without pools (nano, mini) the thread's this.pools is empty in the worker, and the worker's own warning would only reach the worker's console. This note is raised on the page instead. Load a build with pools: mini-pool, lite, standard, spa or full. Warning severity (console.warn, never throws); development-only.

3D-101 A view was created before three.js was handed over
Trillium

The extension never imports three.js; the page passes its own module once with wildflower.three.use(THREE). view() was called before that, so there was nothing to build a raycaster or colors with. Call use(THREE) first, in the module that imports three.js, as Loading it shows. Thrown on every build.

3D-102 A view needs a name, a renderer, a scene and a camera
Trillium

view(name, options) registers a store under name, so the name is required, and it draws options.scene through options.camera with options.renderer, so all three are too. The same code covers a renderer with no canvas, a fit that is not an element, such as a selector string, a draw option that is not a function (renderer, scene, camera), a render other than 'frame' or 'change', and a maxFps that is not a number above 0. The message says which it is. A view shows the call. Thrown on every build.

3D-103 The view's name belongs to a store that is not a view
Trillium

A view is a store, and store names are shared across the app. Another store, one of your own or a thread, already has this name, so no view was created and that store was left as it was. Choose a name no other store uses (see A view). Thrown on every build.

3D-104 instanced() was given the wrong arguments
Trillium

instanced(source, mesh, spec) takes a pool handle or a function returning an array, a three.js InstancedMesh, and a spec whose position lists the entity's x, y and z fields. In the spec, quaternion is four field names, rotation three, and scale, when it is an array, three; any other length, or anything but field names, is this error. A spec sets the rotation one way, so quaternion or rotation beside another of quaternion, rotation, direction and rotationY is this error too (direction beside rotationY is allowed, and direction is drawn). A sync other than 'frame' or 'change' is this error as well. The message says which argument is wrong. Drawing data shows a binding. Thrown on every build.

3D-105 buffer() was given the wrong arguments
Trillium

buffer(source, mesh, spec) takes a function returning a flat array (a worker's frame, say), a three.js InstancedMesh, and a spec with the record stride, the position offsets within a record and a count(arr) function. The message says which is missing. A quaternion in the spec is four whole-number offsets within a record for x, y, z and w, and it replaces direction: a quaternion of any other shape, or one given with direction, is this error too. Frames from a worker shows a buffer binding. Thrown on every build.

3D-106 More entities than the mesh holds
Trillium

An InstancedMesh holds the number of instances it was created with. The binding's source has more entities than that, so the first ones that fit are drawn and the rest are not. Create the mesh with a count at least as large as the most entities you expect, as in Drawing data. Printed once per binding. Warning severity (console.warn, never throws); development-only.

3D-107 The extension loaded before the framework
Trillium

three.wf.js ran before the framework did, so there was no instance to attach wildflower.three to. Order the tags: the framework, then the extension, then the code that uses it, with defer on the classic scripts so the order holds (see Loading it). Warning severity (console.warn, never throws); development-only.

3D-108 A view by that name already exists
Trillium

view() was called again with the name of a view that already exists, usually because the same code ran twice. The existing view was returned, and the options passed this time (a different renderer, scene or camera) were ignored. Create each view once, or unregister it first with wildflower.unregister(name) to replace it (see Removing a view). Warning severity (console.warn, never throws); development-only.

3D-109 The extension was loaded a second time
Trillium

three.wf.js ran again on a page where it was already installed, usually from a duplicated script tag or a hot reload. The first copy keeps running and the second does nothing, because a second copy would leave the frame loop drawing only the views the first one knew about. Load the file once, as Loading it shows. Warning severity (console.warn, never throws); development-only.

3D-110 The framework build has no frame loop
Trillium

A view draws from the framework's frame loop, which is part of the pool module. The nano and mini tiers leave it out (wildflower.features.pools is false), so a view created there registers its store but never draws. Load a tier with pools: mini-pool, lite, core, spa or full, as Loading it lists. Warning severity (console.warn, never throws); development-only.

3D-111 An entity changed without telling the pool
Trillium

A binding with sync: 'change' writes its pool only after the pool's version goes up, and an entity field changed in place does not raise it. Once a second the development build compares what a write would produce with what is drawn, for every mapped field, and the first difference prints this warning with the binding, the field and the entity's key. Call pool.markDirty(key) or pool.update(key, props) after the change, or use sync: 'frame' for a pool that tick() animates, as Drawing data describes. Warning severity (console.warn, never throws); development-only, once per binding.

3D-112 A uniform binding with no target value or no source function
Trillium

view.uniform(target, source) writes into target.value, so the target is an object with a value: a TSL uniform() node, or one entry of a material's uniforms (material.uniforms.glow, not material.uniforms). The source is a function returning the value. See Shader parameters. Error severity (throws an Error carrying code); every build.

3D-113 A uniform's source threw
Trillium

The view reads a uniform binding's source every frame. When it throws, the uniform keeps its last value, the rest of the frame goes on, and the development build prints the error's message once per binding. Fix the function; a common cause is reading a store before it exists. Warning severity (console.warn, never throws); development-only, once per binding.

3D-114 A video binding with no video texture
Trillium

view.video(texture) takes a three.js texture whose image is a <video>, such as new THREE.VideoTexture(video), not the <video> itself. See Video textures. Error severity (throws an Error carrying code); every build.

3D-115 view.draw() was called while the view was drawing
Trillium

view.draw() draws one frame now, and a frame calls the view's before() and its draw option. A draw() from inside either would start the frame again without end, so it does nothing. Inside the draw option, render with the renderer or composer it is given, and call view.draw() only from code outside the frame (see Drawing only when something changes). Warning severity (console.warn, never throws); development-only, once per view.

KN-100 A Kinetic call was given a wrong argument
Kinetic

A call to scene(), seek(), config(), check(), render(), spring(), ramp(), mix(), random(), noise(), transition(), matchPaths(), shape() or one of the text and data helpers received an argument of the wrong type or outside its range, such as an fps of 0, a samples below 1, a waitFor that is not an array of query names, or a codec that is not an avc1, av01 or vp09 string. check() also throws it when the page has no composition root or the root has no data-duration, and render() throws it when there is no composition root to draw or the browser cannot encode the codec at the composition's size. transition() throws it for an unknown kind, a direction the kind does not take, or a p that is not a finite number. Every function that takes the time first (spring(), ramp(), pose(), posterize(), loop(), hold(), camera(), shake(), wiggle(), smear(), typewriter(), scramble(), rank(), captions(), beat(), level(), and spectrum()) throws it for a t that is not a number or is NaN, with the call as the where, such as spring(t); shake(), wiggle(), smear(), and rank() also refuse an infinite t. When t is an object, the development build's message adds that this is Remotion's shape, as in spring({ frame, fps }), and gives Kinetic's, spring(t, options). format() throws it for a locale or options Intl.NumberFormat refuses, with format(n, { locale }) or format(n, opts) as the where, and captions() for a maxWords or maxChars that is not a whole number from 1, a maxGap or hold below 0, or a sentences that is not a boolean. matchPaths() throws it for fewer than two paths, an empty path, path data the SVG grammar rejects (the development build's message gives the index), or paths with different numbers of subpaths, and shape() for a sides or points that is not a whole number, which a track between two counts produces; matchPaths() morphs between counts instead. lottie() throws it for an animation without goToAndStop() and addEventListener() or a bad start, rate or loop, and animatedImage() for a src that is not a string and in a browser without WebCodecs' ImageDecoder, where a plain <img> can show the file instead. In interactive mode, step() throws it for a bad option, wait: false with until, or a set value that is not a string, number, boolean or null; go() for a step that is not there; config({ mode }) for a mode other than auto, interactive and video; and a scene's follow for an ease that is no spring, overshootClamping, a spring with no damping, or a target that holds no number. A step script's path whose store does not exist, or that names no state field, fails readiness with it instead, so ready() and render() reject and check() fails with reason: 'readiness failed'. The message gives the call and the argument or option, as in render({ audioBitrate }), and the development build's message also says what it expected. Correct the call. Thrown on every build, as an Error whose code is KN-100.

KN-101 waitFor on a build without queries
Kinetic

A scene declares waitFor, and the framework build on the page has no queries, so there is nothing to wait for and frame 0 is not held. Queries are part of the full tier only. Load wildflower.full.min.js, or remove waitFor from a scene that reads no query. Warning severity (console.warn, never throws); development-only.

KN-102 The extension was loaded a second time
Kinetic

kinetic.wf.js ran again on a page where it was already installed, usually from a duplicated script tag or a hot reload. The first copy stays in use and the second does nothing, because a second copy would run a second clock that seeks the same store twice. Load the file once. Warning severity (console.warn, never throws); development-only.

KN-110 The composition has no root, or the root has no duration
Kinetic

Either the page has no element with data-composition-id, or the root has no data-duration. In the first case time has no duration, and in the second the renderer has no length to render, because a time-driven composition has no timeline to take one from. Give the root data-composition-id, data-start="0", data-duration in seconds, data-width, and data-height. The check runs once the page has loaded. Warning severity (console.warn, never throws); development-only.

KN-111 With the HyperFrames runtime on the page, the root has no data-no-timeline and no registered timeline
Kinetic

The HyperFrames runtime is on the page, and the composition root has neither data-no-timeline nor a timeline registered under its data-composition-id, so the renderer may wait for a timeline that never arrives. Add data-no-timeline to the root. config({ channel: 'timeline' }) registers a timeline the runtime can seek instead. The warning fires only when the HyperFrames runtime is on the page, the only case that needs the attribute; a composition that Kinetic plays and renders by itself leaves it out. Rendering through HyperFrames has the setup. Warning severity (console.warn, never throws); development-only.

KN-112 A scene has no timing ancestor
Kinetic

A scene element has no data-start and no ancestor with data-start or data-composition-id, so there is no clip start to subtract and the scene's local equals the composition time. Put the scene inside the composition root, or give it data-start and data-duration of its own. Warning severity (console.warn, never throws); development-only.

KN-113 A data-start reference matches no element
Kinetic

A clip's data-start holds a reference such as intro or intro + 0.5, and no element has that id or data-composition-id, so the clip starts at 0. The message gives the reference that did not resolve. Correct the spelling, give the referenced clip that id, or write the start in seconds. Warning severity (console.warn, never throws); development-only.

KN-114 A clip starts or ends between two frames
Kinetic

A clip's resolved data-start, its data-end, or its data-start plus data-duration, times the composition's fps, is not a whole number. A render captures whole frames only, so the clip appears or disappears on the later frame, and a cut meant for that instant comes a fraction of a frame late. In a render with a shutter, the samples of the frame before that fall after the instant show the change, so the clip appears or disappears part-way through that frame. The message gives the clip, the attribute, its value and the two nearest frame times. Move the time onto a frame, which is a frame number divided by the fps; at 30 fps every multiple of 0.1 s is one. Timing has the rules. Warning severity (console.warn, never throws); development-only, once per clip, checked on each seek.

KN-115 A transition attribute cannot be applied
Kinetic

A clip's data-clip-transition, data-clip-enter or data-clip-exit has an unknown kind, a token that is no direction the kind takes, length or ease, two of one token, a length on data-clip-transition, no length on data-clip-enter or data-clip-exit, or a length longer than the clip. It is also raised for a clip with both data-clip-transition and data-clip-enter, for data-clip-enter or data-clip-exit on an element with no data-start, for a token the kind does not take (a url() on any kind but luma) or luma with no url(), and for a luma, halftone or dissolve transition on a clip whose own box is empty while it holds elements, since the transition's filter covers only that box and nothing of the clip shows; give that clip a size. Kinetic leaves the clip without any of its transitions, and the message gives the clip, the attribute, its value and what is wrong. Correct the attribute; The attributes has the tokens. Warning severity (console.warn, never throws); development-only, once per clip, checked on each seek.

KN-116 A data-clip-transition overlaps no sibling clip, or more than one
Kinetic

A clip with data-clip-transition starts while no sibling clip runs, or while two or more do, counting only siblings with data-start that end before the clip ends. The transition's length is the overlap with one sibling, so with none it has no length and with several Kinetic cannot tell which one the clip replaces, and the clip is left without it. The message gives the clip, its start and the siblings it overlaps. Start the clip before the one it replaces ends, as in data-start="intro - 0.5", and use data-clip-enter for a clip that comes in over nothing. The attributes has the rule. Warning severity (console.warn, never throws); development-only, once per clip, checked on each seek.

KN-117 A luma transition's map failed to load or decode
Kinetic

The image in a luma transition's url(path) could not be fetched, came back with an HTTP error such as 404, or is not an image the browser decodes, so the transition would have no map. The message gives the map's path, the clip and the attribute, and the development build's message the reason. A map from another origin needs an Access-Control-Allow-Origin header from its server, since an SVG filter draws nothing from another origin's image, and a page opened from a file cannot fetch at all; the development build's message says which when the fetch fails. Correct the path, serve the folder over http (from localhost while you work), serve the map from the page's own origin or with that header, or save it as a PNG or JPEG. A map that failed is fetched again by the next check() or render(). Luma transitions covers the maps. On every build, the message is printed once with console.error, ready() and render() reject with an Error whose code is KN-117 and check() fails with reason: 'readiness failed', the readiness entry is held open so a render stops at its timeout, and the composition root gets data-kinetic-ready="failed". Each of them begins with a run of readiness that fetches the map again for a clip still on the page that asks for it, so once the map loads, or the clip is removed or given another map, the next one passes and the root is "true" again.

KN-118 A data-step element is no step, or two steps clash
Kinetic

An element with data-step under the composition root has no id or no data-start, so it is no step; two steps have one id, so step() applies to both and go() enters only the first; a step sits inside another step; two steps start together, so the first has no range; the first step starts after 0; or a step starts at or past the composition's end, so it has no frame of its own, and go() to it holds on the last frame. A number in data-start counts from the composition, not from the step around it, so an inner step cuts the outer step's range in two, and interactive mode never shows the time before the first step. The message gives the step's element. Give each step an id of its own and a data-start, put a step beside the other one and time it from it, start two steps at different times, start the first step at 0, and start every step before the composition's end. Steps has the rules. Warning severity (console.warn, never throws); development-only, once per element and problem.

KN-120 A malformed keyframe track
Kinetic

A track given to keyframes, pose() or interpolate() breaks one of the track rules. at and to are arrays of the same length, two or more, at holds numbers that increase, and to holds all numbers, all colors (#rgb, #rrggbb, #rrggbbaa, rgb() or rgba()), or all text with only its numbers and colors changing; the message gives the track and the part of it that failed, as in keyframes.title.opacity.at[2], and for text the development build's message gives the first place two keyframes differ. A color or text track takes no unit and no identity extrapolation. The transform keys and blur take numbers, and a transform track beside a transform key, or a filter track beside blur, throws too. In SVG path data the command letters and arc flags are text, so two paths with different commands or flags differ, and the development build's message ends by pointing to matchPaths(), which converts any two paths to the same commands. Tracks and pose() and Path data in a track have the rules. Thrown on every build, when the scene is defined for keyframes and at the call for the other two.

KN-121 An unknown ease name, or an ease array of the wrong length
Kinetic

An ease on a track, or an argument to ease(), interpolate() or ramp(), is a string that is not an ease, or a value that is not a string, a function or { spring }. Use a CSS name (ease-out, cubic-bezier(...), steps(n)), a GSAP name such as power2.out or back.inOut, spring, or a function of progress. A cubic-bezier() with an x value outside 0 to 1 is also unknown. An ease array on a track or in interpolate() needs one entry per segment, one fewer than the keyframes, and each entry is one of the above; the message gives the track and the entry's index. For an unknown name, the development build's message gives the closest ease, reading the names other tools use (easeInOut and Easing.inOut are power2.inOut, easeOutCubic is power2.out), and lists the eases. Thrown on every build.

KN-122 A track gives a length property numbers with no unit
Kinetic

A track in keyframes or pose() sets a property that takes a CSS length, such as width, top, fontSize, borderRadius, offsetDistance, a margin or a padding, from numbers, and has no unit. A length with no unit is not valid CSS, so the browser ignores the value and the property stays unset, with no error. lineHeight is warned about as well, because a bare number there is a multiple of the font size. Add unit: 'px', or another unit, to the track, or unit: '' to a lineHeight meant as a multiple. Tracks lists the units pose() adds itself. Warning severity (console.warn, never throws); development-only, once per track, when the scene is defined or pose() first reads the track.

KN-123 A spring is cut off by its next keyframe
Kinetic

A segment of a track eases with a spring, and the next keyframe arrives while the spring is more than 2% of the distance between the two keyframes from rest, so the value jumps to that keyframe. A spring runs on the seconds since its keyframe, and only on the last segment of a track does it continue past the keyframe. The message gives the track, the segment's times, how far from rest the spring is in the track's unit, and how long the spring takes to come within 2% of rest. Lengthen the segment, or give the spring a track of its own with two keyframes. Easing covers springs. Warning severity (console.warn, never throws); development-only, once per track, when the scene is defined or pose() first reads the track.

KN-130 An input failed its type or validator
Kinetic

A scene's inputs declaration did not accept the composition variable. The variable is required and missing, cannot be read as a Number or Boolean, or its validator returned false or threw. The check runs when the scene first reads this.inputs and again when readiness checks every scene. Fix the variable or the declaration. Printed with console.error on every build, with the scene and the input, and in the development build what is wrong with the value. ready() rejects with an Error whose code is KN-130, the readiness entry is held open so a render stops at its timeout, and the composition root gets data-kinetic-ready="failed", also for a scene mounted after the first frame. check() and render() each begin with a run of readiness, so while the scene is on the page check() fails and render() rejects with it, for a bad input in any one scene; once the scene is removed, both pass.

KN-131 A waitFor query ended in error
Kinetic

A query listed in a scene's waitFor failed, so frame 0 has no rows to draw. The message gives the scene and the query, and the development build's message the query's own error. Fix the query's source, or take the query out of waitFor. On every build, the message is printed once with console.error, ready() rejects with an Error whose code is KN-131, and the readiness entry is held open, so a render stops at its timeout with the message in its diagnostics. check() and render() each begin with a run of readiness that reads the query as it is, so they fail with it while the scene is on the page, and pass once the query has loaded or the scene is removed. After the first frame that run waits for a query that is loading again, and a refresh that ends in an error is KN-131 for check() and render() too, though the rows the query had are still there.

KN-132 A waitFor query reloaded after frame 0
Kinetic

A query listed in a scene's waitFor went back to loading, or became stale, after frame 0, so a render can capture frames before the new rows arrive. A refresh interval, invalidate() and refresh() all cause it. Load the data once and leave the query alone while the composition renders. In interactive mode on a page with steps it is off, since a lesson refreshes its data; check() and render() run in video mode, where it is on. Warning severity (console.warn, never throws); development-only, once per query.

KN-133 A waitFor name matches no query
Kinetic

A scene's waitFor lists a name that no wildflower.query() call registered, so readiness fails. The message gives the scene and the name. Correct the name, or register the query. On every build, the message is printed once with console.error, ready() rejects with an Error whose code is KN-133, and the readiness entry is held open, so a render stops at its timeout with the message in its diagnostics. check() and render() each begin with a run of readiness, so they fail with it while the scene is on the page, and pass once the query is registered or the scene is removed.

KN-134 An input's default differs from the composition's
Kinetic

A scene's inputs entry has a default, and the composition's data-composition-variables declares a different default for the same variable. The declared default is a variable value, so it is what the input reads, in a render and in a plain tab, and the input's own default never shows. The message gives the scene, the input and both defaults. Keep the default in one place: drop it from the scene's inputs, or make the two agree. inputs has the rule. Warning severity (console.warn, never throws); development-only, once per scene and input.

KN-135 An audio clip's file failed to load or decode
Kinetic

An <audio> clip's src could not be fetched, came back with an HTTP error such as 404, or is not a format the browser decodes, so the render would have no sound for it. The message gives the clip and the src, and the development build's message the reason. A file on another origin is read only when its server sends CORS headers, and a page opened from a file cannot fetch at all, and the development build's message says which when the fetch fails. Correct the path, serve the folder over http (from localhost while you work), serve the file with CORS headers, or convert it to a format the browser decodes, such as WAV, MP3 or AAC. A footage clip with data-has-audio="true" is read the same way, and a video file with no sound track is KN-135 too: the development build's message says the file has no sound track and suggests muted in place of data-has-audio. Audio has the clip attributes. On every build, the message is printed once with console.error, ready() and render() reject with an Error whose code is KN-135 (render() with audio: false too), check() fails with reason: 'readiness failed', the readiness entry is held open so a render stops at its timeout, and the composition root gets data-kinetic-ready="failed". Each clip that uses the file has its own KN-135, whose message gives that clip. check() and render() each begin with a run of readiness, which asks for a file that failed again, so once the file is on the server, or the clip is given another file that decodes or is taken off the page, the next one passes and the root is "true" again.

KN-136 An audio clip attribute is not applied
Kinetic

An <audio> clip has loop, autoplay or controls, which this version of Kinetic does not apply, so the preview and the render play the clip once. Kinetic plays the clip itself, so the element's own playback from autoplay or controls would play it a second time. data-playback-rate is applied, with the pitch moving with the rate. A data-remap Kinetic cannot read, or one whose ease overshoots, is not applied, so the clip plays at data-playback-rate; and a data-playback-rate beside a data-remap is not applied, since the remap's keys set the speed. The message gives the clip and the attributes. Remove them, and for a sound that repeats, write one <audio> per repeat. Audio has the attributes Kinetic reads. Warning severity (console.warn, never throws); development-only, once per clip.

KN-137 level() or spectrum() has no audio to read
Kinetic

level() or spectrum() was called after a decode that found no audio clip in the composition, so it returns 0, or zeros, and whatever it drives stays still. The cause is a missing <audio data-start> inside the composition root, or a clip added after the page loaded, which await wildflower.kinetic.ready() decodes. level() and spectrum() covers the levels. Warning severity (console.warn, never throws); development-only, once per page.

KN-138 A footage clip's file failed to load or decode
Kinetic

A <video> clip's src could not be loaded, came back with an HTTP error such as 404, is in a format the browser cannot play, or failed to decode, or the clip has no src and no <source>, so the render would have no picture for it. The message gives the clip and the src, and the development build's message the reason, which it finds by asking for the file once. Chrome plays H.264, VP9 and AV1 video in an MP4 or WebM file. A <video> plays a file from another origin without CORS headers, so for such a file the development build's message says whether its server answered, with no CORS advice; a render does need CORS (KN-204). On a page opened from a file, the development build's message says the file is missing or cannot be played. Correct the path, or re-encode the file, for example with ffmpeg -i in.mov -c:v libx264 -c:a aac out.mp4. It is also a clip that loaded but whose seek has not landed on its frame in 30 s, as when its server stops sending the file partway: the seek, and a check() or render() waiting on it, fail with KN-138 naming the clip, so a render never waits forever. Footage has the clip attributes. On every build, the message is printed once with console.error, ready() and render() reject with an Error whose code is KN-138, check() fails with reason: 'readiness failed', the readiness entry is held open, and the composition root gets data-kinetic-ready="failed". check() and render() each begin with a run of readiness, which loads a clip that failed again from its source, so once the file is on the server, or the clip loads from a corrected src or is taken off the page, the next one passes and the root is "true" again. With the HyperFrames runtime on the page, the runtime loads and plays media itself, and Kinetic does not wait for footage.

KN-139 A footage clip attribute is not applied, its rate is clamped, or it starts past its file's end
Kinetic

A <video> clip has markup Kinetic does not apply, or a rate it clamps. autoplay, controls and loop would play the element on its own while Kinetic seeks and plays it. data-automation holds speed ramps, which Kinetic does not apply, so the clip plays at data-playback-rate. A clip with neither muted nor data-has-audio="true" plays no sound in Kinetic, and HyperFrames needs one or the other. A clip with both contradicts itself, and Kinetic plays its sound, as data-has-audio asks. A data-playback-rate outside 0.1 to 10 is clamped to that range. A data-remap Kinetic cannot read, or one whose ease overshoots, is not applied, so the clip plays at data-playback-rate; a data-playback-rate beside a data-remap is not applied; and with the HyperFrames runtime on the page, which does not apply data-remap, the clip plays at its rate there. A clip with no data-duration or data-end whose data-media-start (or data-playback-start) is at or past the end of its file lasts no time, so it is never shown; Kinetic says so once the file's length is known, without the runtime. The message gives the clip and each attribute, with a suggestion for each. Remove autoplay, controls and loop, and write one <video> per repeat; keep muted for the picture alone, or data-has-audio="true" for its sound, one of the two. Footage has the attributes Kinetic reads. Warning severity (console.warn, never throws); development-only, once per clip, with or without the HyperFrames runtime.

KN-140 A wall clock was read during a seek
Kinetic

Your code called Date.now(), performance.now() or new Date() between a seek and its frame capture. A frame has to be a function of the seek, and a value read from the wall clock differs on every run, so a render captures a different frame each time. Derive the value from local or time. The warning includes the line that made the call. Warning severity (console.warn, never throws); development-only, once.

KN-141 Math.random() was called during a seek
Kinetic

Your code called Math.random() between a seek and its frame capture. It returns a different number on every run, so a render captures a different frame each time. Use wildflower.kinetic.random(seed) for a sequence, or noise(x, seed) for a drift. The warning includes the line that made the call. Warning severity (console.warn, never throws); development-only, once.

KN-142 A timer, fetch or animation frame request started during a seek
Kinetic

Your code called setTimeout(), setInterval(), fetch() or requestAnimationFrame() between a seek and its frame capture. A frame is captured when the seek settles, so the work shows in a later frame or in none. Load data before frame 0 with waitFor and compute the rest inside the seek. The warning includes the line that made the call. Warning severity (console.warn, never throws); development-only, once.

KN-143 A CSS transition is running under the composition root
Kinetic

A CSS transition is running on an element under the composition root. It advances on the wall clock, so a render captures it part-way. Replace the transition with a keyframes track, or an interpolate() call in a computed, so the value is a function of local. In interactive mode on a page with steps it is off, since the learner starts hover and press transitions; check() and render() run in video mode, where it is on. Warning severity (console.warn, never throws); development-only, once.

KN-145 Locale-dependent formatting during a seek
Kinetic

Your code called toLocaleString(), toLocaleDateString(), toLocaleTimeString(), Intl.NumberFormat or Intl.DateTimeFormat with no locale between a seek and its frame capture. The formatting then follows the render machine's locale, so a number or date reads differently on another machine. Pass a locale such as 'en-US', or use wildflower.kinetic.format(), which defaults to en-US. Warning severity (console.warn, never throws); development-only, once.

KN-146 A scripted path was written in video mode
Kinetic

In video mode a step's script writes its store paths on every seek, and something else wrote one of them: a slider dragged during a render, a watcher, or the page's own code. A frame captured then would show that value instead of the script's, so Kinetic writes the script's value back within a microtask, before the next paint. A watcher runs inside the write that set it off, so one that writes a scripted path inside the script's own write is found when Kinetic compares the paths with what it last wrote and reads them back after its writes. A path is put back once at most between two seeks, so a watcher that writes it again every time keeps its value after that one correction, with this warning printed. The message gives the path and the step whose script writes it. Disable the control in video mode, as with data-bind-attr="{ disabled: $kinetic.mode === 'video' }", and keep watchers from writing a scripted path. Scripts has the rule. Warning severity (console.warn, never throws); development-only, once per path. The write is undone on every build.

KN-150 A pool in a scene that the scene did not make static (retired)
Kinetic

Retired with WildflowerJS 1.5.5, and no longer raised. Every seek now flushes the page's pools with wildflower.flushPools() before the frame is captured, so a pool in a scene is in the frame whenever it mounted, and Kinetic no longer makes a scene's pools static. Earlier versions warned when a scene contained a data-pool element without data-pool-static. Pools has the rule.

KN-151 A scene with tick() is in a video
Kinetic

A scene that defines tick() is on the page during a render(), or while the page is in video mode (the HyperFrames runtime, or config({ mode: 'video' })). A scene's tick(dt) runs only in interactive mode, on the page's frame loop, so what it changes is missing from the video. Keep what the video shows a function of local or time in computeds, and use seek(local) for work that must run once per frame; tick() is for what the interactive page does on its own, such as physics while a step holds. A check() makes no video, so it raises nothing, and an interactive page that uses tick() gets no warning. Warning severity (console.warn, never throws); development-only, once per scene.

KN-152 A scene defines a value the extension provides
Kinetic

A scene's computed, state, keyframes or follow uses one of time, local, clipFrame, frame, progress, remaining, enter, exit, presence, vars or inputs. The extension's value is the one the scene reads, so the definition's own is shadowed. Rename the property. Warning severity (console.warn, never throws); development-only, raised when the scene is defined.

KN-153 A keyframes or follow entry shares a name with another
Kinetic

A scene lists the same name in keyframes and in computed, or in follow and in computed or keyframes. The keyframes entry wins over the computed, and the follow entry over both, so the other one never runs. Keep one of them. Warning severity (console.warn, never throws); development-only, raised when the scene is defined.

KN-154 A binding on the root tag of a scene inside another scene reads the outer scene's value
Kinetic

A scene sits inside another scene, and a binding on its root tag, such as data-bind-style="{ opacity: progress }", uses one of the values Kinetic gives every scene (local, progress, presence and the rest). A binding on a component's root tag is read in the scope of the component around it, here the outer scene, which has the same value, so the binding follows the outer scene's clip and raises no error. Move the binding to an element inside the scene, or declare the root's binding in the definition with root: { style: { opacity: 'progress' } } (class, attr, show and directives work the same way). From 1.5.5, a name only the scene has, inside any component of your own, is reported by WildflowerJS as WF-521. Warning severity (console.warn, never throws); development-only, once per scene.

KN-156 Something else writes a property a transition writes
Kinetic

A clip has a transition, and during a seek a binding on its tag or its scene's root: block wrote one of the properties the transition writes (opacity, translate, scale, rotate, clip-path, filter or z-index) again after Kinetic did. The two overwrite each other, so a frame shows whichever wrote last, and check() can find two seeks of one frame that differ. It is also raised when page code writes one of those properties on the clip between two seeks while the transition runs: that value becomes the clip's own, and comes back after the window. The message gives the clip and the property. Put the scene's own styling on an element inside the scene and leave the clip's tag to the transition. What Kinetic writes has the properties. Warning severity (console.warn, never throws); development-only, once per clip.

KN-157 A step's definition or target has nothing to act on
Kinetic

A lesson has one of four gaps: step() for an id that no data-step element has, so the definition is not used; an until or follow target whose store does not exist at readiness, so the step never goes on by itself or the follower reads 0; a wait longer than the step's range, which interactive mode clamps to the range; or a settings entry that starts after 0 in the first step that scripts its path, so its first value also shows from the start of the video. The message gives the step or scene and the path. Give the clip that id, define the store before the page has loaded, shorten the wait, or start the settings at 0 with the value before it. Interactive mode has the options. Warning severity (console.warn, never throws); development-only, once per step and path, checked when the steps are read and at each run of readiness.

KN-158 The preview bar put a lesson in video mode
Kinetic

A page with steps is in video mode only because the preview bar is up, which is what config({ mode: 'auto' }), the default, does. In video mode the lesson plays as a video: its steps never hold, and its scripts write over what the learner's controls write, so the controls appear to do nothing. For the lesson, remove data-preview from the extension's script tag (or ?preview from the URL), or call config({ mode: 'interactive' }) to keep the bar. It is not raised when check() or render() runs, when the mode is set to 'video', or when a renderer or the HyperFrames runtime decides the mode. Interactive mode has the modes. Warning severity (console.warn, never throws); development-only, once per page.

KN-159 A key read on a keyframes entry's style object that it does not have
Kinetic

A keyframes entry with several tracks is a style object, such as { opacity, transform }, and code read a key it does not have, such as this.rise.translateY, which is undefined. pose() folds the transform keys (translateX, translateY, scale, rotate, and the rest) into transform, and blur into filter. For the number, give the key its own keyframes entry, which is then a number, or call pose() with its track. Only keys a style could hold are watched, a track key or a CSS property, and data-bind-style reads only the keys the object has. Keyframes has the entries. Warning severity (console.warn, never throws); development-only, once per scene, entry, and key. The production build returns the plain object.

KN-160 check() found a failed readiness, an error during the seeks, or a frame that differed between two seeks
Kinetic

check() seeks a sample of frames twice, in different orders, compares the DOM each time, counts the errors printed or thrown during the seeks, and reads the root's data-kinetic-ready. Every report carries ready and errors. With reason: 'readiness failed', the run of readiness check() begins with failed, as the one render() begins with does, or a readiness failure was reported during the seeks, such as a scene a section mounts partway whose input fails, which fails render() during its frames too; render() rejects with the same error, and the report gives its code and where on every build. When the run at page load failed too, a HyperFrames render waits at frame 0 until its timeout. The KN error printed when it failed gives the input, query, clip or file; once that source loads or is taken off the page, check() passes again. With reason: 'a seek threw', a console.error, an uncaught error or an unhandled rejection happened during the seeks, such as a computed that throws, and firstError holds the first message. Fix that error first. Otherwise one frame held different content on each pass, so something in it depends on the order of seeks or on the wall clock. The report gives the frame, its time, the element, the property and both values, and the KN-14x warnings printed during the seeks cover the usual causes. check() does not see a binding that shows nothing or a list with no rows, since those are the same on every seek. check() returns the report on every build. Warning severity (console.warn, never throws); development-only.

KN-170 A seek took longer than 8 ms
Kinetic

A seek took longer than 8 ms to settle, the budget Kinetic allows a seek, about half a frame at 60 fps. Keep per-seek work small, with fewer rows, a static pool and no layout reads in computeds. When several scenes are inside their clips at that time, the message says how many: each one's computeds and seek(local) run on every seek, while a scene outside its clip costs nothing. In a scene with a Trillium view the first seek that draws also compiles the shaders, so call renderer.compile(scene, camera) in init() to move that cost before frame 0. Warning severity (console.warn, never throws); development-only, once.

KN-180 A Lottie animation failed to load
Kinetic

An animation handed to lottie() fired lottie-web's data_failed event, so part of it could not be loaded, such as its JSON from a path that is wrong or that its server answers with an error such as 404. It is KN-180 as well when the animation fires lottie-web's error event before it has loaded: lottie-web loaded the data and could not set it up, and the message adds : its data after the animation. The message gives the animation's name when loadAnimation() was given one, its file, and the scene it was given with scene: this. The development build's message says to check the path, and adds that a file on another origin needs CORS headers (Access-Control-Allow-Origin) when the path is on another origin, or that a page opened from a file cannot fetch its files, so the folder is served over http, such as from localhost; for error it gives lottie-web's own error. Correct the path, serve the file from the page's own origin, or export the animation again. lottie-web 5.13 fires neither event for a file that is not JSON at all, and readiness then waits until the runtime's timeout, with lottie-web's own error in the console. lottie() has the options and what readiness waits for. On every build, the message is printed once with console.error as the event fires, ready() and render() reject with an Error whose code is KN-180, check() fails with reason: 'readiness failed', the readiness entry is held open, and the composition root gets data-kinetic-ready="failed", also when the failure comes after the first frame. Kinetic cannot load the animation again, so the failure stays until it is destroyed or handed back, or its scene is removed; check() and render() each begin with a run of readiness, so the next one then passes and the root is "true" again.

KN-181 An animated image failed to fetch or decode
Kinetic

The file given to animatedImage(src) could not be fetched, came back with an HTTP error such as 404, or could not be decoded as a GIF, WebP or PNG, so no frame of it can be drawn. The message gives the file as it was written. The development build's message gives the reason: the HTTP status, the decoder's error with the type the file's first bytes or its server gave, or the fetch's error. For a fetch that failed, it adds that a file on another origin needs CORS headers (Access-Control-Allow-Origin), or that a page opened from a file cannot fetch its files, so the folder is served over http, such as from localhost. Correct the path, convert the file to GIF, WebP or APNG, or serve it with CORS headers. animatedImage() has the handle and what readiness waits for. On every build, the message is printed once with console.error as the file fails, ready() and render() reject with an Error whose code is KN-181, check() fails with reason: 'readiness failed', the readiness entry is held open, and the composition root gets data-kinetic-ready="failed", also when the failure comes after the first frame. ready(), check() and render() each begin with a run of readiness that fetches the file again, so once it loads the next one passes and the root is "true" again; an image asked for with scene: this is no longer fetched once that scene has left the page.

KN-182 An image did not load in 30 s
Kinetic

An <img> under a scene, or an SVG <image> in the composition, was still loading 30 s after readiness began waiting for it, as when its server accepts the request and never answers. Readiness waits for every image, so without a limit ready(), check() and render() would wait forever. The message gives the image's address as it was written. Check that the server answers, serve the file from the page's own server, or remove the image. An image that fails to load at once is not held against the render; only one that never finishes is. On every build, the message is printed once with console.error, ready() and render() reject with an Error whose code is KN-182, check() fails with reason: 'readiness failed', and the composition root gets data-kinetic-ready="failed". The next ready(), check() or render() waits for the image again.

KN-190 A Trillium view's WebGL context has no preserveDrawingBuffer
Kinetic

A Trillium view whose canvas sits inside a scene draws to a WebGL context created without preserveDrawingBuffer. A canvas like that can read back blank when a frame is captured. Create the renderer with new THREE.WebGLRenderer({ canvas, preserveDrawingBuffer: true }). Warning severity (console.warn, never throws); development-only, once per canvas.

KN-191 An effect's or a drawing's WebGL context was lost
Kinetic

The browser took away the WebGL context of an effect()'s or a draw()'s canvas, so it draws nothing until the browser restores it, and a drawing is drawn again then. A browser drops the oldest context when a page holds too many, which effects that are never disposed, or canvases made again and again, can cause. The message gives the canvas and the kind. Give each effect scene: this, which disposes it with its scene, or call dispose() on one you no longer draw, and keep the number of effect canvases low; Kinetic sets a restored context up again and draws at the next seek. Effects covers the rules. Warning severity (console.warn, never throws); development-only, each time a context is lost.

KN-192 A finish on a page with 16 WebGL layers
Kinetic

render({ finish }) ran while effects and drawings on the page held 16 WebGL contexts. The finish opens one more for the render, and Chrome keeps 16 a page, so it drops the oldest for good and that layer can come out blank in the file. The message gives the number of contexts. Dispose the effects and drawings you no longer draw, or give each scene: this so it is disposed with its scene, and keep a piece with a finish to 15 WebGL layers; a Trillium view holds a context too. The finish covers the limit. Warning severity (console.warn, never throws); development-only, at the start of each such render.

KN-200 render() has no VideoEncoder
Kinetic

render() found no VideoEncoder on the page. WebCodecs needs a secure context and a browser that has it (Chrome 94 and later), so serve the page over https or from localhost. Thrown on every build, as an Error whose code is KN-200.

KN-201 render() cannot draw the page's DOM
Kinetic

render() was given a DOM source, the composition root by default, and this browser has no drawElementImage on 2D canvas contexts. Chrome has it only with HTML-in-Canvas enabled, which Chrome's article on the API covers. Until Chrome 157 that is a setting: paste chrome://flags/#canvas-draw-element into Chrome's address bar, set the HTML-in-Canvas entry to Enabled, and restart Chrome. A canvas passed as source renders in any Chrome. Thrown on every build, as an Error whose code is KN-201.

KN-202 render() wrote the sound as Opus
Kinetic

render() was asked for the default sound codec, AAC, and this browser has no AAC encoder, so the file's sound track is Opus. Chrome on some platforms has no AAC encoder. Chrome plays the file, and more players read AAC than Opus in an MP4. Render in a browser with an AAC encoder, such as Chrome on macOS, or pass audioCodec: 'opus' to choose Opus, which silences the warning. Sound in a render has the options. Warning severity (console.warn, never throws); development-only, once per page.

KN-203 render() could not measure the audio encoder's delay
Kinetic

render() measures the audio encoder's delay before it encodes the sound, so the file can line the sound up with the picture, and the measurement failed. The measuring encoder or decoder reported an error, the measuring impulse did not come back from the decoder, or the encoder gave no codec description. The development build's message gives which. Try the other codec with audioCodec, or render in Chrome, whose encoders Kinetic is tested with, or pass audio: false for the video alone. Thrown on every build, as an Error whose code is KN-203.

KN-204 render() found media from another origin that would be drawn black, or an effect's source WebGL cannot read
Kinetic

The composition shows an image or video from another origin, and Chrome's drawElementImage draws it black in the video, with no error of its own. An <img>, <video> or SVG <image> is drawn once it has crossorigin="anonymous" and its server sends an Access-Control-Allow-Origin header. A <video>'s poster follows the video's crossorigin. A CSS image (a background, mask, border or list image, or content: url(), on the element or its ::before or ::after) from another origin is drawn black even with that header, an <input type="image"> has no crossorigin and is drawn black too, and a frame or an <object> from another origin is drawn empty, so serve them from the page's own origin or as a data: URL. render() checks before it touches the page, open shadow roots included, and the message gives each element with its URL. effect() raises it too, at the first draw from a source of another origin without CORS, since WebGL cannot read that source; the message gives the source and its URL, the fix is the same crossorigin attribute and header, the seek that drew it rejects, and the effect then skips that source with no further error until the source changes (Effects). Thrown on every build, as an Error whose code is KN-204.

KN-205 render() did not apply the shutter, with the HyperFrames runtime on the page
Kinetic

render() was given a shutter, its own or the one config({ shutter }) sets, and the HyperFrames runtime is on the page. The shutter needs Kinetic's own clock. The runtime's player rounds each seek down to one of its own frames, which would put every sample of a frame on the frame's own time. render() therefore seeks once a frame with no motion blur, and the clock store's shutter stays 0, so smear() keeps its copies. For an in-browser render with motion blur, remove the runtime's script tag from the page, or render without the shutter. The shutter has the details. Warning severity (console.warn, never throws); development-only, once per page.

KN-206 render() found a frame the HyperFrames player does not seek to
Kinetic

With the HyperFrames runtime on the page, render() seeks through the runtime's player, which puts every seek on a grid of frames at the rate it read from window.__HF_EXPORT_RENDER_SEEK_CONFIG as it started, 30 without one. Each frame's seek has to land on the frame's own time, and one landed somewhere else, so the file would repeat or skip frames. Either the render's fps does not divide the grid's rate, or from falls between two of the grid's frames. The message gives the time sought and where the seek landed, and render() puts the page and the clock back before it throws. When the page sets no config, Kinetic defines one that reads as the root's data-fps, and the player uses that rate when Kinetic has loaded and the root is in the page before the runtime starts. Kinetic's tag before the runtime's, plain or deferred, is in time. A Kinetic script added after the page has loaded, or a deferred Kinetic tag after a deferred runtime's, comes too late, and with no config set the player stays at 30. config({ fps }) runs too late for the runtime, so a root that takes its rate from it leaves the player at 30 too. A render whose fps divides the root's data-fps goes ahead: a root at 60 renders at 60 or 30, and one at 25 at 25. Give the root a data-fps that the render's fps divides, with Kinetic's script tag before the runtime's and the root in the page when the runtime starts; or set window.__HF_EXPORT_RENDER_SEEK_CONFIG = { fps } in a script before the runtime's; or remove the runtime's script tag for an in-browser render. The runtime, for a HyperFrames render has the details. Thrown on every build, as an Error whose code is KN-206.

KN-207 render()'s video encoder failed
Kinetic

The browser's video encoder reported an error, took no frame from its queue for 10 seconds, or gave output the MP4 cannot hold, such as H.264 chunks with no decoder configuration. The message names the codec, with the frame for a stall, or names the MP4, and the development build adds the encoder's own message or what the MP4 lacked. render() puts the page and the clock back before it rejects. Another codec, a smaller size or a lower bitrate may encode. Thrown on every build, as an Error whose code is KN-207.