Skill 18 · Webflow Mcp:interactions
Subchapter 18.9
references/rejects-index.mdMarkdown28 KBView on GitHub
Match the fragment in the error against this table, then read the linked file.
The same rule can produce different wording depending on which surface refused
it. The page-automation tool layer validates before the DE host does, and it
phrases some messages differently — it also deliberately softens
validateActionPropertyShape so a get-then-update round trip is not rejected
before the host runs.
If a fragment below does not match your error exactly, check the other family:
| Surface | Example wording |
|---|---|
Guard (designerInvariants) | Trigger "wf:navbar" is not in the Designer's default capability set (it is gated behind a feature flag) and cannot be created through the API. |
| MCP tool layer | Trigger type "wf:navbar" is not in the Designer's default capability set and cannot be used. |
| Fragment | Cause | Read |
|---|---|---|
cannot be combined | A standalone trigger shares the interaction | scroll, mouse-move |
at most one | Second wf:load | load |
must not carry a target | Load purity | load |
must not set pluginConfig | Load purity | load |
requires a trigger target | Scroll with no target | scroll |
requires a "scrollTriggerConfig" | Scroll config missing or null | scroll |
must not set "scrollTriggerConfig" | Config on a non-scroll trigger | scroll |
endTrigger is not offered / scroller is not offered / horizontal is not offered | Unauthorable scroll setting | scroll |
pin must be a boolean | Tuple pin | scroll |
the Designer shows no playback settings for it | control/delay/jump/speed on scroll or continuous | scroll, mouse-move |
must not set a jump when control is | Jump with a clearing control | click |
must use control "play" when the interaction has multiple action groups | Grouped timelines need Play | timelines |
requires controlType | Explicit controlType contradicts the registry | updating |
requires a triggerMetadata.role | Missing role on a role-routed trigger | timelines |
Expected one of: mouseX, mouseY, interval | Bad mouse-move role | mouse-move |
Expected one of: open, close | Bad navbar/dropdown role | gated |
without a boolean "multiTimeline" / legacy field | Hover config models mixed | hover |
must use the hidden "wf:body" | Custom trigger target | custom |
requires a target element | A trigger in TRIGGER_REQUIRES_TARGET_KEYS (click, hover, focus, blur, change) sent with no target. From the missing-target branch of findTriggerInvariantError, not findTriggerTargetContextError | click, hover |
is only valid on timeline actions | Action-only target key on a trigger | envelope |
does not support Filter | Active filter on wf:trigger-only / wf:inst | envelope |
is not offered by the Designer (shouldShow: false in all contexts) | wf:id | envelope |
is not offered by the Designer's Filter-by picker | Bad key inside filterBy | envelope |
target.value is required | Missing value — use '' | envelope |
matches multiple style blocks; use a style-block id array instead | A wf:class name string reused as a leaf across combo chains. Host normalization, not a guard | envelope |
does not match a style block on this site / is not on this site | A wf:class name or id that does not resolve. Host normalization, not a guard | envelope |
is not a supported | Property not on the allowlist for its key | actions |
is non-animatable and must only be used in a Set action | Needs tt: 3 | actions |
must not be a plain {from, to} object | Use an array | actions |
does not support random values / random min/max / additive values | Value mode unsupported for that property | actions |
random-array must have between | Random set size | limits |
is not a start time the Designer can author | GSAP operator or bad ms string in timing.position | actions |
must match (splitText mask) | A valid mask that differs from type. mask: 'none' is a schema failure, not this | actions |
cannot be set on a scroll-scrub interaction | timing.repeat / yoyo under scrub | scroll |
timelineDefaults is not authored by the Designer | Any non-null timelineDefaults. Omit on create; null on update clears a stored bag | envelope |
the runtime uses playInReverse, not autoReverse | A new or altered timing.autoReverse / settings.autoReverse | envelope |
must not set assignedTimelineRole | Panel writes assignedGroupId instead | timelines |
matches no timeline groupId | A discrete standard trigger routed to a group no timeline claims. null, a matching id, and role-routed triggers are all fine; load/scroll/continuous are exempt. Unchanged stored pairings grandfather | hover, timelines |
does not exist on this site | A scope componentId no component matches, or a component-scoped wf:inst whose element is missing from the definition. Also used for missing pages | envelope |
has no variant option(s) | A scope variants entry that is not one of the component’s variant options. Ids are variant option ids, not names | envelope |
must provide a component id / variants must be an array | Component scope with a non-string componentId, or variants that is not an array | envelope |
valid on component-scoped interactions | A [componentDefinitionId, elementId] wf:inst path on a site- or pages-scoped interaction. Page and site targets must be [pageId, elementId] | envelope |
only authored on interval mouse-move timelines | distance / axes on a non-interval role, or with no mouse-move trigger | mouse-move |
duplicate action id | A second action reusing an id to ride a stored legacy splitText allowance | actions |
An interaction may define at most | A per-interaction cap. Through MCP this is Zod .max() on the argument schema and applies even to over-cap stored data; the host’s baseline-aware ceiling is host-only | limits |
is not in the Designer's default capability set | Gated trigger or action | gated |
not yet default-on | Trigger conditionalLogic | gated |
actions.0.id: Required | Missing action id — schema, not a guard | envelope |
Some payloads fail Zod before any invariant runs, so the message names a format rather than the Designer. These have no guard to cite.
| Fragment | Cause | Read |
|---|---|---|
Absolute position must be a number or milliseconds string | timing.position as '1.5s', a bare '500', or another unsupported string. Operator forms pass schema and are refused by the guard instead | actions |
Milliseconds must be in format "123ms" or "123.45ms" | Any secondsOrMsSchema field given '1.5s', a bare '400', or another unsupported string. Not just timing.duration: also delay, repeatDelay, jump, and stagger.amount / stagger.each. A number is already seconds, so 400 is four hundred seconds, not 400ms, and is accepted. null is accepted too and becomes 0.25 | actions |
Invalid input on timing.position | NaN. Zod’s z.number() refuses it before the guard runs; Infinity passes schema and the guard catches it instead | actions |
Invalid input at splitText | mask: 'none' or any value outside chars / words / lines. splitText is parsed as a union, so the top-level issue is a bare Invalid input at the field rather than an enum message. A single-field endpoint may surface the nested enum detail. Omit mask instead | actions |
Expected number on scrollTriggerConfig.scrub | scrub: true or scrub: false. Use 0 to scrub without smoothing, omit to disable | scroll |
Expected integer, received float on distance | A fractional interval distance | mouse-move |
Number must be greater than or equal to 1 on distance | Interval distance below the minimum. Note this is a min/max message, not Invalid input | mouse-move |
Number must be less than or equal to 10000 on distance | Interval distance above the maximum | mouse-move |
actions.0.id: Required | Missing action id | envelope |
A payload that saves and then does nothing has no message to match against. These
are the known causes, and none of them errors, warns, or reads back differently —
get_interaction echoes exactly what you sent. Some rows are now historical: the
write path has since grown a stamp or a rejection, so they explain stored data you
inherit rather than a payload you can still create. Those say so in the cause column.
| Symptom | Cause | Read |
|---|---|---|
| A scroll reveal never plays | No enter on a non-scrub scroll, written before the host stamped absent toggles. Only repaired on the next write through the host | scroll |
| A scrub barely moves, or an authored start value is ignored | [from, to] on a To (tt: 0). Only the to half reaches GSAP | actions |
| A scrub plays in a sliver of the range | Action timing.duration is smaller than canvasDuration | scroll |
| A “scrub” plays all at once when the range is crossed | scrub omitted. That shape is a one-shot play, not a scrub | scroll |
| The first click does nothing | control: 'reverse' on a playhead that starts at 0. Use togglePlayReverse | click |
| A grouped interaction written through MCP never runs | Written before groupId was accepted on the timeline input, so triggers point at groups no timeline claims. New writes are rejected instead | timelines |
| A class target animates nothing | The ids do not form one combo chain, so the compound selector matches no element | envelope |
| A trigger element cannot be clicked or hovered | Its own from-state collapses the box (scaleX: 0, width: 0) | actions |
| Mouse-move never fires | No trigger target. Validation accepts none and bindTrigger resolves nothing | mouse-move |
action.timing.delay has no effect | Inert on actions. Use timing.position | actions |
| A hover in/out authored as the panel’s trigger split never runs | Same stored cause as the grouped row above. Authoring it fresh now either works or is rejected | hover |
A reveal that plays but is never seen is a different problem with the same
appearance: check start before assuming the interaction is broken. See
trigger-scroll.md.
An empty read is a third case with the same quality of silence. list_interactions
returning {items: [], total: 0} is neither a reject nor a silent write: the tool
reads IX3 only, so a site whose interactions are IX2 reads exactly like a site
with none. Confirm before building as though there is no existing motion.
Only the first violation is reported. findTriggerInvariantError fixes the
order guards run in, and findConditionsCapabilityError runs early on purpose so
“not offered” wins over deeper outcome-shape messages. Fixing one error can
surface another that was always present — that is expected, not a regression.
Read the guard directly in
packages/systems/ix3/schema/src/designerInvariants.ts. Every guard cites the
Designer source it mirrors, which is usually a faster explanation than the message
itself. If you add a rule, add its fragment to this table.