Skip to content

Troubleshooting

Start from what you have: a symptom you saw (a view that will not update, a caret that jumps), or an error Fresco raised, which carries a stable :rf.error/… id. Fresco calls its errors complaints.

Start from a symptom

The most common ones:

Symptom Cause Fix
A view does not re-render when app-db changes The value was not read with h/sub during the view's render (for example an explicitly framed rf/subscribe-once, or a value read once and kept in a callback) Read it with h/sub in the view body
A view called as (todo-row {:id 7}) ignores its props, or fails with React's invalid-hook error A defview is a React component that only React may call; Fresco raises nothing for this Write [todo-row {:id 7}]; use a plain defn for a helper you call
A dispatch from a timeout or promise raises :rf.error/no-frame-context The callback runs after rendering, with no frame in scope Capture the frame with (rf/capture-frame) while rendering and use its :dispatch, or dispatch an effect that carries the frame
A controlled field drops characters or moves the caret The edit was dispatched asynchronously, or the field left Fresco's controlled path Dispatch the edit event directly from :on-input
Clearing a field does nothing The model value did not change, so the field saw nothing to update Advance ::h/revision when you reset it
List rows keep the wrong state after reordering Rows are keyed by index, or not keyed Put a stable id in :key
Clicking an href="#" link jumps to the top of the page Fresco prevents the default only for :on-submit Wrap the event: [::h/prevent [:todo/set-showing :done]]

Every chapter ends with its own troubleshooting table. Go to the chapter for what you were working on:

Working on Table
Getting a project to build or boot Installation, Getting started
Views, reads, and what re-renders Views and reads, Lists and collections
Event vectors and callbacks Events as data
Text fields, carets, and IME Controlled inputs, Forms
URLs and navigation Routing and navigation
Fetching, mutations, and races Async resources
A foreign React library Interop
A React island Islands
Local UI state Ephemeral state
Animation and enter/exit Motion and presence
Modals, popovers, and focus Overlays and focus
Theme and locale Theming and internationalisation
Tests Testing
Xray and evidence Diagnostics
Error boundaries Errors
Server rendering and hydration SSR and hydration
Something being too slow Performance
A Reagent codebase Migrating from Reagent
Lazy loading and chunks Code splitting and lazy loading
Keyboard, screen readers, and roles Accessibility

Start from a complaint

A Fresco error is a thrown ex-info. Its message is the reason with the id in brackets, and the id is in ex-data:

(try
  (render-the-thing)
  (catch :default e
    (let [{:rf.error/keys [id] :keys [where reason]} (ex-data e)]
      (js/console.error id where reason))))

Every error Fresco raises carries four keys, except evidence/envelope's refusal, which carries no id:

Key What it tells you
:rf.error/id Which error this is. Branch on this and nothing else
:where Which function refused
:reason Why, in a sentence, for a human
:recovery Fresco's own refusals say :no-recovery, and the fix is in :reason; ids raised through core name a specific recovery. Branch on the id, not on this

In a development build, errors raised while a view renders also carry :view and :source: the view and the file and line of its defview. They are absent in a release build, so use them for debugging and never branch or assert on them.

In tests and error monitoring, match on the id, never the message. Messages improve between releases; an id never changes and is never reused.

The complaint index

Every error the package raises, grouped by the feature that raises it. Ids that core, routing or the SSR module define say which one defines them.

If an id is not here, it is raised by another part of re-frame2 (core, routing, resources), or your application and test-kit versions do not match.

Hiccup, heads and children

Taught in Views and reads.

:rf.error/fresco-empty-vector

You wrote [] where hiccup was expected. Give the vector a head, or write nil where you meant nothing.

:rf.error/fresco-bad-head

You put something in hiccup head position that is not a tag keyword, :<>, :>, a defview view or a defhost host — most often a plain function, or a raw React component that needs defhost or [:> Component …]. To use a plain function, call it; to make it a head, make it a defview.

:rf.error/fresco-true-child

You let true reach child position, usually a predicate result such as (= id selected) written as a child. Return markup or nil instead, for example with when.

:rf.error/ui-tree-malformed

You let a value outside the structural-tree grammar reach an L2 tree or a projection. Defined by the SSR module.

Fix the template or the runtime value, which the message names.

Key warnings

These two are development-build console warnings rather than thrown complaints. Each fires once per site. Taught in Lists and collections.

:rf.warning/fresco-missing-key

A seq of view children crossed into a boundary with no :key. React's own missing-key check does not run on that path, so the list reconciles by index and row state follows the wrong row when the order changes. Key each child on a stable domain id: [child {:key (:id entity) …}].

:rf.warning/fresco-entity-key

A view child's :key is a map, vector or foreign object rather than a stable identifier. React turns it into a string, so the child remounts whenever the entity changes, and every foreign object collapses to [object Object]. Key on the entity's id instead.

Reads and the render extent

h/sub works only while a view body is running, because that is when Fresco records what the view reads. These complaints mean a read happened outside that window, or the body did something a body may not do.

Taught in Views and reads.

:rf.error/fresco-sub-outside-render

You called h/sub outside a view body, for example in a callback, a promise or a lazy sequence realised later. Read the value in the body and pass or close over the result.

:rf.error/ambient-frame-refused

You called rf/subscribe or rf/dispatch in a view body without naming a frame. Read with h/sub; dispatch through an event vector, h/event, or the :dispatch of (rf/capture-frame). Defined by core.

:rf.error/fresco-deferred-read-at-boundary

You let an unforced delay reach a boundary's props. The child would force it during its own render, the delay would cache the value, and the read would be lost on the next render.

Pass a function instead: the child calls it on every render, so its reads stay tracked.

:rf.error/fresco-generation-fence-exhausted

A boundary body saw a new commit land during each of four consecutive runs — usually because the body writes to app-db, directly or through a synchronous dispatch, every time it renders.

Move the write out of the render, into an event.

Roots

h/render! takes root options only. Frame configuration is written in the tree.

Taught in Installation.

:rf.error/fresco-frame-config-misplaced

You passed :frame or :initial-events in h/render!'s options. Put them on the head that takes them: [h/frame-root {:id … :initial-events […]} …] to create the frame, or [h/frame-provider {:frame …} …] to scope one that already exists.

:rf.error/fresco-unknown-root-option

You passed h/render! options that are not a map, or a key other than :hydrate? and :identifier-prefix. Pass a map holding only those two keys.

:rf.error/no-adapter-installed

A frame was built before an adapter was installed. Make (rf/init! substrate/adapter) the first line of boot; in Node, call (rf/init! ssr/adapter) before server/render. Defined by core.

Frames

A frame is carried, never looked up. These fire when something rendered or dispatched with no frame in scope, a frame head was given the wrong key, or a frame could not be built.

Taught in Events as data.

:rf.error/frame-root-given-frame

You gave h/frame-root a :frame key. frame-root creates a frame and takes :id; to scope an existing frame, use h/frame-provider. Defined by core.

:rf.error/frame-provider-given-id

You gave h/frame-provider an :id key. frame-provider scopes an existing frame named by :frame; to create one, use h/frame-root. Defined by core.

:rf.error/frame-provider-frame-absent

You scoped a frame that does not exist. Make it first — rf/make-frame, or an h/frame-root above — or, when hydrating, make it before ssr/hydrate!. Defined by core.

:rf.error/frame-root-reconfigured

You re-rendered a mounted h/frame-root with a different :id or options — most often a reload that dropped :initial-events. Pass the same options every render; to switch frames, change the React :key so the boundary remounts. Defined by core.

:rf.error/initial-events-step-failed

An :initial-events step threw while a frame was being built, whether handed to hm/mount! or hm/hydrate! or to an h/frame-root creating its frame. The frame is torn down and nothing was mounted, so there is no handle to tear down. Fix the event the error names in its :step-index and :event. Defined by core.

:rf.error/no-frame-context

You rendered a Fresco boundary or island hook whose React context carries no frame, or dispatched from a timeout or promise with no frame in scope. Defined by core.

Nothing falls back to a default frame. Render inside an h/frame-root or h/frame-provider, or capture the frame with (rf/capture-frame) while rendering and use the captured :dispatch in the callback.

Intents and callback positions

An intent is an event vector at a handler position. These fire where the position cannot turn it into a dispatch.

Taught in Events as data.

:rf.error/fresco-intent-outside-boundary

An event vector was turned into a callback outside any view's render, for example inside a function a foreign component calls later. Keep event vectors in the Hiccup a view returns; inside a foreign callback, use h/event.

It is also raised at render when an overlay's :on-dismiss, or an h/error-boundary's vector :on-error, has no frame above it. Mount the region under h/frame-root or h/frame-provider, or give :on-error a function.

:rf.error/fresco-intent-needs-the-event

You wrote an event vector that reads a DOM event (a ::h/value marker, say) at a foreign callback whose first argument is a value rather than a DOM event. Use h/event, which receives every argument the caller passed, in order.

:rf.error/fresco-malformed-prevent

You wrapped something other than exactly one event vector in ::h/prevent. Write [::h/prevent [:todo/set-showing :done]].

Controlled inputs

Taught in Controlled inputs.

:rf.error/fresco-revision-not-controlled

You put ::h/revision on something that is not a controlled text field. It belongs on an :input or :textarea with :value.

:rf.error/fresco-file-input-value-marker

You read ::h/value from a file input, where .value is a fake path such as C:\fakepath\photo.jpg, not the files. Use h/event and read (.. e -target -files).

Error boundaries

Taught in Errors.

:rf.error/fresco-boundary-unknown-prop

You wrote a key outside h/error-boundary's closed roster — a misspelled :on-error is an error boundary that reports nothing. Use only :fallback, :reset-key and :on-error.

:rf.error/fresco-boundary-bad-on-error

You gave h/error-boundary an :on-error that is neither an intent vector nor a function, so nothing could fire it. Pass an event vector such as [:todo/record-failure], or a function.

Hosts and the raw escape

A defhost declaration is checked when the namespace loads. The raw [:>] escape has no declaration, so its error is raised where it renders.

Taught in Interop.

:rf.error/fresco-host-no-component

You declared a defhost over nil, usually a JavaScript import that resolved to nothing. Check the import name and whether it is a default export.

:rf.error/fresco-bad-host-declaration

You wrote a defhost declaration outside its shape, and the reason names which: options that are not a map (usually a docstring written after the component instead of before it); an option outside #{:callbacks :slots :server :fallback}; a :callbacks contract outside :event and :render; a :slots value that is not a set of ordinary prop names (a non-set, an entry that names no prop, key/ref, one slot spelled twice, or a position that is also a declared callback); or a form after the options map, which is discarded rather than merged. Write the declaration as (h/defhost name docstring? component opts?) and correct the part the reason names.

:rf.error/fresco-host-bad-ssr-policy

You gave a defhost a :server value outside the two it admits, or a :fallback the policy beside it cannot carry. Declare :server :client-only (the default), optionally with a :fallback, or :server :render with no :fallback.

:rf.error/fresco-host-fallback-boundary-head

You put a defview or defhost head inside a declared fallback.

Plain hiccup in the fallback, or :server :render to render the real subtree on the server.

:rf.error/fresco-host-unclaimed-callback

You wrote h/event at a defhost prop declared in :slots. A slot takes markup, not a function.

Write the markup there, or take the position out of :slots.

:rf.error/fresco-raw-not-a-component

You handed the raw escape nil in component position — usually a :default import that resolved nothing — or a Fresco defview or defhost head, which is a head in its own right.

Write [:> Component props & children] with the real component, or write the head as [my-view …]. Any other invalid type is React's own error at render.

Routing

Taught in Routing and navigation.

You rendered a route link outside any frame. Render it inside a view under an h/frame-root or h/frame-provider.

You gave a route link an :on-click that is not nil, a [::h/prevent [:some/event …]] veto, an h/event, or a plain function. A bare intent vector is refused because the click already dispatches the navigation. To replace the navigation with your own event, wrap it: [::h/prevent [:some/event …]].

You gave a route link :prefetch :intent and also your own value at :on-mouse-enter, :on-focus or :on-touch-start — the three positions :prefetch fills. Drop :prefetch and write the prefetch yourself at the positions you are not using, or move your handler off those positions.

You gave a route link a :prefetch value other than :intent. To make the link passive, omit :prefetch. Defined by routing, which raises it for both h/route-link and rf/route-link.

:rf.error/routing-artefact-missing

You rendered a route link with routing absent. Defined by core.

Add day8/re-frame2-routing to your dependencies and require re-frame.routing at boot, before frames are constructed.

Server rendering

Taught in SSR and hydration.

:rf.error/ssr-missing-payload-policy

You called server/render without :payload. Pass an allowlist vector of top-level app-db keys, or :rf.ssr.payload/whole-app-db to send everything. Defined by the SSR module.

:rf.error/ssr-render-failed

server/render or server/render-body completed, but the runtime recorded an error it recovered from during the pass — a subscription that threw, say — so the markup is not trustworthy. Fix the surface the error record names. Defined by the SSR module.

:rf.ssr/hydration-mismatch

A development warning trace, not a throw. A hydrating root's first client render differed from the server markup, and React replaced that root's DOM. Keep view bodies deterministic, and put every value both sides render in the payload.

Motion and presence

Taught in Motion and presence.

:rf.error/fresco-presence-child-unkeyed

You gave motion/presence a child with no :key, or a child that is not a hiccup vector. Write each child as a keyed hiccup vector, such as [:li {:key id} …].

:rf.error/fresco-presence-timeout-required

You gave motion/presence no :timeout-ms, or one that is not a positive number. Pass a positive :timeout-ms that covers your exit transition.

Overlays and focus

Taught in Overlays and focus.

:rf.error/fresco-overlay-anchor-missing

You gave an overlay an :anchor naming a DOM id no element in the document carries. Omitting :anchor is fine: a modal takes none, and a popover without one uses the default position.

Generate a unique, stable trigger id from the instance id, and render the trigger in the same tree as the overlay so the two arrive in one commit.

Ephemeral state

Taught in Ephemeral state.

:rf.error/fresco-state-bad-argument

You gave h/reg-state a concern that is not namespace-qualified, or options outside {:default …}; or you used an instance key outside the accepted set (nil included) at a read or a write. The reason names which. Use a qualified keyword concern, options holding only :default, and an instance key that is a keyword, string, number or vector of those.

The test kit

L2 runs one view body as a data tree with no React running. It throws when the thing being tested is not visible at that level; the fix is usually to test at the next level up (L3, mounted).

Taught in Testing.

:rf.error/fresco-test-not-a-body

You gave an L2 tree form a head that is not a defview body. Put a defview view, or the body function it is defined from, in head position.

:rf.error/fresco-test-not-a-render-form

You gave an L2 tree something other than a hiccup form. Pass a non-empty vector, [view props & children].

:rf.error/fresco-test-plain-fn-head

You put a plain function in a hiccup head inside an L2 tree. Define it with h/defview, or pass it as the root form of ht/tree to run that body alone.

:rf.error/fresco-test-boundary-body-not-retained

You gave an L2 tree a defview head in a build that erased its body. Run view tests in a development build, or pass the body function instead of the head.

:rf.error/fresco-test-bad-option

You gave a test-kit door non-map options, or an option outside its closed roster: #{:subs} for an L2 tree; :initial-events, :images, :container and :clock for hm/mount!; those and :html for hm/hydrate!, whose promise rejects with it rather than throwing; and :reference, :candidate, :images, :initial-events and :script for hm/shadow!. hm/shadow! also raises it for a script step other than {:click selector} or {:type [selector text]}. Remove the key or step the message names. hm/advance-clock! raises it on a handle whose hm/mount! or hm/hydrate! was not given {:clock true}, or whose mount has come down; pass {:clock true} to the mount that makes the handle.

:rf.error/fresco-test-bad-reads

You gave an L2 tree a :subs option that is not a query-to-value map. Pass a map from query vector to value, such as {[:todo/by-id 7] {:title "Milk"}}.

:rf.error/fresco-test-missing-read-fixture

You let an L2 body read a subscription no fixture answers. Add a fixture for the exact query vector the message names.

:rf.error/fresco-test-host-is-opaque

You let a defhost crossing reach the L2 semantic tree. Test the view at L3 with hm/mount!.

:rf.error/fresco-test-react-is-opaque

You let a raw React element reach the L2 semantic tree. Test the view at L3 with hm/mount!.

:rf.error/fresco-test-not-a-host

You read the declared server policy off something that is not a defhost. Pass ht/host-policy a var defined with h/defhost.

:rf.error/fresco-test-not-a-native-form

You gave an L1 projection a form whose head is not a tag keyword. Pass a native form, such as [:input {:on-input [:todo/edit ::h/value]}].

:rf.error/fresco-test-not-an-intent

You gave the L1 marker materializer something other than an intent vector. Pass ht/materialize an event vector.

:rf.error/fresco-test-not-a-dom-node

You gave the canonical-DOM comparator something that is not a DOM node. Pass a DOM node, such as a mounted container; compare two L2 trees with =.

:rf.error/fresco-test-no-handler-at-position

You fired at a prop position the form does not write. Fire at one of the positions the message lists.

:rf.error/fresco-test-position-is-not-a-handler

You fired at a position whose value converts to something other than a function. Fire at an on* prop that holds an event vector, a key map or a function.

:rf.error/fresco-test-l1-dispatch

You invoked a handler converted by a pure L1 projection. Use ht/fire!, which dispatches into a real frame, or test the handler at L3.

:rf.error/poll-until-timeout

The predicate given to hm/settle-until! never held before its :timeout-ms (default 2000). The ex-data carries :elapsed-ms and your :label. Check that the predicate can become true for what the mount renders, and raise :timeout-ms only for work that is slow. hm/hydrate! rejects with it too, when the root's adoption has not finished within the budget its third argument sets (default 3000 ms); the ex-data then carries :elapsed-ms, :budget-ms and the frame. Defined by core.