Skip to content
MocklyDocs

Reference

Diagnostics

Every error and warning the compiler reports, with its code.

Every problem the compiler finds is a diagnostic: a stable code, a severity, a one-sentence message that names the specific thing, the smallest piece of source that shows the problem, and usually a hint on how to fix it. The studio shows them as you type, and the CLI prints them or returns them as JSON.

Errors and warnings#

An error means the site can't be built as written, so publishing stops until it's fixed. A warning points at something that's probably a mistake but doesn't stop a publish, such as a link to a page that doesn't exist or a goal that nothing records.

Some diagnostics include a fix the studio can apply for you, such as replacing == with ===.

What a diagnostic contains#

This is what mockly check --json returns for an experiment without a control variant:

JSON
{  "ok": false,  "diagnostics": [    {      "code": "MK4005",      "severity": "error",      "message": "Experiment \"cta\" needs exactly one variant named \"control\".",      "file": "pages/index.tsx",      "range": {        "start": { "line": 7, "column": 8, "offset": 158 },        "end": { "line": 7, "column": 18, "offset": 168 }      },      "hint": "Name the baseline variant \"control\"."    }  ]}

ok is false when any diagnostic is an error. Lines and columns start at 1.

Codes never change#

A code always means the same problem, so tools, scripts, and AI agents can rely on it. When a rule is removed, its code is retired and never reused. Retired codes are listed below for reference.

Codes starting with TS are TypeScript's own errors, such as TS2367 when a comparison can never be true, with TypeScript's message. Codes starting with MK are Mockly's, grouped by area:

MK1xxx: files, statements, expressions

CodeSeverityMessage
MK1001error<kind> statements aren't allowed in a component body.Components contain only const declarations and one final return. <kind> is the statement in words, such as with or debugger , never TypeScript's name for it.
MK1002error<kind> isn't allowed at the top level of a file.Allowed: imports from "mockly", const data, components, and types. <kind> is in words, such as "A switch statement".
MK1003errorImports from "<source>" aren't allowed.Import built-ins from "mockly", and site files with a relative path such as "../components/hero".
MK1004error"mockly" has no export named <name>.Available: Page, Experiment, Variant, Show, For, Form, Window, Timer, state, ref, media, track, fetchJson, derived, secret.
MK1005errorlet and var aren't allowed.Use const, or state() for a value that changes.
MK1006errorComponent <Name> must end with return <markup>.
MK1007error<name> isn't defined, or isn't an allowed global.
MK1008errorCalling <callee> isn't allowed.Allowed calls are listed in the language reference. For a method that changes its list in place (sort, reverse, splice, push, …), names the one that returns a copy (toSorted, toReversed, toSpliced, a spread). For setTimeout and setInterval, points to <Timer>.
MK1009errorAccessing <prop> isn't allowed.
MK1010error<syntax> isn't supported in Mockly.Also for a function that isn't a handler, a <For> child, or a list method's callback; a callback with a block body; anything but an arrow function written in place where a list method takes its callback; and new other than new Date(…). Also for a name generated code relies on, declared or given to an export: see docs/02 §5.
MK1011errorUse === instead of == (or !== instead of !=).Includes a fix.
MK1012errorAssignment is only allowed inside event handlers.
MK1013error<name> is already declared.Rename one of the declarations.
MK1014errorRendering failed: <detail>.Reported by the publisher when a page throws while rendering, at the source of the failing line. Also by the API's render pool, at the page (the preview's, or the first), when a check, preview, or publish doesn't finish: "Rendering failed: it took longer than <N> seconds." past its time limit (15 s for a check or a preview, 60 s for a publish), and "Rendering failed: the renderer stopped." when its worker process exits, such as out of memory.
MK1015errorFile path "<path>" isn't valid: <reason>.Includes a [parameter] with a name that isn't camelCase or appears twice, and two routes that match the same paths (/posts/[slug] and /posts/[id]).
MK1016errorpaths in source: <route> has [<param>] in its path, so it needs export const paths = …; Only a page with a [parameter] in its path exports paths; Export paths where it's declared; paths is data … it can't be state; <name> isn't a parameter of this page .e.g. export const paths = posts.map((p) => p.slug); in pages/posts/[slug].tsx.
MK1017error paths of <route> <problem>. (isn't a list, lists more than 1,000 pages, has an entry that isn't a string or an object, gives a value that isn't 1 to 100 letters, digits, -, _, or ~, lists a page twice, or gives a page another page already is)Reported by the publisher at paths.

MK2xxx: markup

CodeSeverityMessage
MK2001error<tag> elements aren't allowed.Use <Page> props for title and description; styles come from utility classes.
MK2002errorUnknown element <tag>.
MK2003errorThe <attr> attribute isn't allowed.In any case: srcDoc is srcdoc.
MK2004errorUse <html> instead of <react> (for example class instead of className).Includes a fix.
MK2005errorSpread attributes aren't supported.Pass each attribute explicitly.
MK2006errorEvent handlers must be inline arrow functions.e.g. onClick={() => { open.value = true; }}
MK2007errorURL "<url>" uses a blocked scheme.Allowed: http, https, mailto, tel, and relative URLs.
MK2008errorA page must export default a component whose root is <Page>.
MK2009errorInvalid attribute name <name>.
MK2010error<Page> can only be the root of a page component.
MK2011errorThe child of <For> must be one arrow function that returns markup.e.g. {(item) => <li>{item}</li>}
MK2012errorEvent <name> isn't supported.Supported: onClick, onDblClick, onInput, onChange, onSubmit, onKeyDown, onKeyUp, onFocus, onBlur, onMouseEnter, onMouseLeave, onPointerDown, onPointerUp, onPointerEnter, onPointerLeave, onScroll, onToggle, onClose, onCancel, onClickOutside.
MK2013error<Component> needs a <prop> prop.Also for <Form> without name.
MK2014warning"<url>" doesn't match a file in public/.Upload it to public/, or use an absolute URL. Checked for URL attributes, <Page icon> and image, and root-relative file URLs in any string, such as a constant.
MK2015warning"<url>" doesn't match a page in this site.Lists the site's pages.

MK3xxx: state and islands

CodeSeverityMessage
MK3001errorstate() must be used as const x = state(initial), directly in a component body or at the top of a file.At the top of a file, every island on the page shares it.
MK3002errorRead <name>.value instead of using the state object directly.
MK3003retiredRetired: islands can render other islands. The code stays reserved and won't be reused.
MK3004retiredRetired: islands take markup and children. The code stays reserved and won't be reused.
MK3005retiredRetired: handlers and reactive expressions can read <For> loop variables. The code stays reserved and won't be reused.
MK3006retiredRetired: a <For each> that reads state inside an island is a list that changes in the browser. The code stays reserved and won't be reused.
MK3007errorA state's initial value can't read other state.
MK3008errorA value derived from state can't be passed as a prop to <Child>, which renders when you publish. Also: <Name> gets props that change in the browser, so it can't pass them to <Component>, which renders when you publish, or on to <Inner>, another island; <Name> gets props from this island's state, so this island starts it (no load).Inline the markup in the island, or make it an island: an island's props can follow state.
MK3009errorIsland <Name> must return a single root HTML element. Also: A page can't be an island.Wrap the content in a <div>. For a page: move the state, refs, handlers, or shared state reads into their own component.
MK3010errorconst <name> reads state, so it would go stale.Make it const <name> = derived(() => …) and read <name>.value.
MK3011errortrack() can only be called inside an event handler.For clicks, use the track="goal" attribute.
MK3012error<prop> is markup, but island <Name> reads it in a handler or a reactive expression.Markup passed to an island can be rendered, not used in its client code.
MK3013errorref() must be used as const x = ref() directly in a component body. Also: ref={<name>} must name a const <name> = ref() of this component.Then put ref={x} on the element.
MK3014error<name> is a ref: handlers can call focus(), blur(), select(), scrollIntoView(), showModal(), show(), and close() on it, and nothing else.e.g. onClick={() => { input.focus(); }}
MK3015errorRef <name> must be on exactly one element: <detail>.Put each ref on one element, outside <For>. The detail is that it's inside a <For>, already on another element, or on no element.
MK3016error<Window> can't have children, or needs an event handler. Also: <Window> can't be <where> (inside <Show>, <For>, markup passed to a component, or a <Form> message).It takes only event handlers: onKeyDown, onKeyUp, onClick, onPointerDown, onScroll, onResize, onFocus, onBlur. For a placement: put it directly in the island's own markup, and check state in the handler to listen only sometimes.
MK3017error<Timer> <detail>: takes every or after, not both; needs a time or an onTick handler; can't have children; onTick gets no event; its time must be a number, and as a literal must be from 50 to 2147483647 ms; when must be an expression. Also: <Timer> can't be <where>, as for MK3016.e.g. <Timer every={5000} onTick={() => { slide.value += 1; }} />, or after={…} to run once.
MK3018error<prop> has handlers or reactive parts of island <Outer>, but it ends up inside island <Inner>.An island binds only what's under its own root, so <Inner> would take them as its own. Move them out of this markup, or into <Inner> (shared state can connect the two). Reported where the markup is passed, also when plain components forward it.
MK3019errorRemembered state <detail>: options that aren't an object, an unknown option, a key that isn't a valid string literal, no key, values on a boolean or missing on a string or number, a non-literal or uncookieable value, mixed types, a repeated value, fewer than 2 or more than 8 values, or an initial value outside them. Also: Only state at the top of a file can be remembered. Remembered state "<key>" is declared more than once. This page reads remembered state with <n> combinations of values (<keys>), more than 16. (Markup's uses of a value without values other than as it is were MK3019 until D-118, which made them wait for their island.)Shows the form it expects, e.g. state(false, { remember: "banner-closed" }). For a value without values: give it values to render it any way.
MK3020errorA list that changes in the browser: Each item of a list that changes in the browser must be one HTML element. Also, for islands in items: <Name> is an island in an item of a changing list, so it can't be inside another <For> of the item, can't take markup, or starts with its item (no load); <Name> is an island in a changing list's items, so it can't render <Inner>, can't pass its props to <Component>, or can't listen on the whole page. <Component> renders when you publish, so its props can't read <item>, which changes in the browser. onClickOutside can't be used inside a list that changes in the browser. key must be an arrow function that names an item. key can't read state.Wrap the item in one element, e.g. {(item) => <li>…</li>}; keep islands outside the list; put the parts that change in HTML elements; e.g. key={(item) => item.id}.
MK3021errormedia() must be used as const x = media("(…)") directly in a component body, with the query as a string. Also: Read <name>.value: it's true while the media query matches. <name>.value can only be read in event handlers and <Timer when>.The last: markup that depended on the screen would change as the page loads; use CSS, e.g. md:hidden or motion-reduce:transition-none.
MK3023errorderived() must be used as const x = derived(() => expression), directly in a component body or at the top of a file. Also: <name> is derived, so it can't be assigned. It recomputes when the state it reads changes; assign the state it's computed from instead.
MK3022errorload must be "visible" or "idle". Also: <Name> is an island, so load says when the page starts it; the island can't read it as a prop.load="visible" starts the island when it scrolls into view, load="idle" when the browser has nothing else to do; leave it out to start it with the page. For the prop: give it another name.

MK4xxx: experiments and goals

CodeSeverityMessage
MK4001error<Experiment> needs <prop> as a string literal.
MK4002error<what> name "<name>" is invalid.Use lowercase letters, digits, and dashes; start with a letter or digit; 63 characters max.
MK4003retiredRetired: experiments may share a name. See MK4017.
MK4004error<Experiment> must contain 2–8 <Variant> children and nothing else.
MK4005errorExperiment "<name>" needs exactly one variant named "control".
MK4006errorVariant "<name>" appears more than once in "<experiment>".
MK4007errorweight must be a positive integer literal. / The weight of variant "<name>" must be a positive integer literal. (variants)
MK4008errorExperiments can't be nested.
MK4009error<Experiment> can't be inside <context> (an island, a <For>, or a reactive <Show>).
MK4010warningGoal "<goal>" is never tracked anywhere in the site.Add track="<goal>" to the element that converts.
MK4011error<Variant> must be a direct child of <Experiment>.
MK4012errortrack must be a string literal goal name. Also: track() takes a goal name as a string literal and, optionally, a value.e.g. track("signup"), or track("purchase", total) to send a value
MK4013error<Form> needs <prop> as <what>.name and goal must be string literals; redirect a page path such as "/thanks" or an http(s) URL.
MK4014errorForms named "<name>" are declared with different settings.Every <Form> with one name must have the same goal and redirect.
MK4015errorForms can't be nested.A <Form> or <form> inside another, in the same component.
MK4016error<Page> needs goal as a string literal goal name.e.g. <Page title="Thanks" goal="signup">
MK4017errorExperiments named "<name>" are declared with different settings.Every <Experiment> with one name must have the same goal, the same secondary goals, and the same variants with the same weights. related points at the first declaration. They must also agree on the metric and the guardrails.
MK4018warningVariant "<variant>" of "<experiment>" renders exactly like control.Visitors who get it see no difference, so it can't win. Change what it shows, or remove it. Reported by the publisher at the experiment's first declaration.
MK4019errorsecondary must be a list of goal names as string literals. / Goal "<goal>" is listed twice in secondary. / secondary can't include "<goal>", the experiment's goal. / secondary lists <n> goals; an experiment can watch at most 5.e.g. secondary={["demo-request", "pricing-view"]}
MK4020errorvariants must be 2–8 variant names with weights, as an object literal, or the names alone, as a list, for an even split.e.g. variants={{ control: 50, green: 50 }} or variants={["control", "green"]}
MK4021errorWith variants, an <Experiment> takes one child: a function of the variant that returns markup. / A function child needs variants on its <Experiment>, to say which variants there are.e.g. {(v) => <a class={v === "green" ? "bg-green-600" : "bg-blue-600"}>Start</a>}
MK4022errormetric must be "conversions" or "revenue"."revenue" compares revenue per visitor, from the values sent with the goal: track("purchase", total), or mk_value in the address of a page goal.
MK4023errorguardrails must be a list of goal names, or an object of goal names to "drop" or "rise". / Guardrail "<goal>" must be "drop" or "rise". / guardrails can't include "<goal>", the experiment's goal. / Goal "<goal>" is both a secondary goal and a guardrail. / Goal "<goal>" is listed twice in guardrails. / guardrails lists <n> goals; an experiment can have at most 5.e.g. guardrails={["purchase"]}, or guardrails={{ purchase: "drop", "refund-request": "rise" }}
MK4024warningVariant "<variant>" of "<experiment>" gets 0% of traffic: its weight, <weight> of <total>, rounds to 0 when the weights become whole percentages.Nobody will see it. Give it a larger weight, or remove it. At the variant's weight.
MK4025errorheatmap must be written as heatmap, heatmap={true}, or heatmap={false}.heatmap records this page's heatmap; heatmap={false} leaves it out when the site records every page's. At the attribute.
MK4026warningThe not-found page doesn't record a heatmap.It's shown at any address nothing else has, so its clicks can't be told apart. Remove heatmap.

MK5xxx: components and modules

CodeSeverityMessage
MK5001error<name> isn't a known component. Component names start with an uppercase letter.
MK5002errorComponents take at most one parameter: props.
MK5003errorComponent <Name> renders itself, directly or indirectly.
MK5004errorAnother component is already named <Name>.
MK5005errorImport cycle: <a> → <b> → <a>. (M5)
MK5006error"<spec>" doesn't match a file in this site.
MK5007error"<spec>" is a page, and pages can't be imported.
MK5008error<file> has no export named <name> (or no default export).
MK5009error<file> exports <name> through more than one export *.

MK6xxx: data

CodeSeverityMessage
MK6001errorWhere and how data is fetched: fetchJson() must be used as const x = fetchJson("https://…") at the top of a file, or as state(fetchJson(…), { refresh }) there. ; fetchJson <detail>. (the URL: a string literal, absolute http(s), no credentials, at most 2048 characters); fetchJson options <detail>. (method, headers of string literals or secret(), a JSON-literal body with POST, headers the request or browsers reserve); Data fetched again in the browser <detail>. (refresh is true or 1000 to 86,400,000 ms, its request has no secret, it isn't remembered, and only fetchJson state takes it); secret() can only give a header of fetchJson its value… ; "<name>" isn't a secret's name. It's fetched when you publish, and the constant holds what came back; examples of each form.
MK6002errorCouldn't fetch <url>: it <detail>. (answered a non-2xx status, didn't send JSON, sent more than 5 MB, didn't answer within 10 seconds, nested more than 64 levels, had a NUL character, couldn't be reached, or redirected where it can't follow)Reported by the publisher at the fetchJson() call. Pages render with this data when you publish, so publishing waits for it. Check the URL, and that it answers with JSON.
MK6003errorThe secret <NAME> isn't set.Reported by the publisher at the fetchJson() call. Set it in the studio's Data view, under Secrets, or share it with the site from the organization's Secrets page; the mockly CLI reads it from MOCKLY_SECRET_<NAME>.
MK6004errorThe secret <NAME> can't be sent to <origin>: it's only sent to <origins>.Reported by the publisher at the fetchJson() call, before the request is made, so the secret isn't sent. Each secret goes only to the origins set for it. If that origin should get it, add it to the secret in the studio: the site's Data view, under Secrets, or the organization's Secrets page for a shared one.

MK7xxx: the site theme and the visual editor

CodeSeverityMessage
MK7001error components/site-theme.tsx is the site theme, and it doesn't export siteTheme. At the file's start, or at a siteTheme declared without export.Write export const siteTheme = { colors: { … }, text: { … } }; there, or give the file another name if it's something else.
MK7002errorThe site theme <detail>. (must be an object written out, { colors, text }; has <key>, and a theme has only colors and text; has <key> twice; <key> must be an object written out; must be written out, with no spreads, computed keys, shorthand properties, or methods)It's read when compiling, so it's an object written out: colors and text, of names and string values.
MK7003errorTheme color "<name>" <detail>. (isn't a valid name; is one of UnoCSS's own colors; would change what classes UnoCSS already has mean, such as text-<name> or border-<name>; is in the theme twice; isn't a color: "<value>"; must be a color, such as "#4f46e5", or shades of one; has a shade "<shade>"; has the shade <shade> twice; has a shade <shade> that isn't a color; has no shades)How names are written, a name of its own instead of UnoCSS's, or how a color is written: "#" and 3 or 6 hex digits.
MK7004errorText style "<name>" <detail>. (isn't a valid name; would change what text-<name> means, a class UnoCSS already has, or the theme's color "<color>"; is in the theme twice; must be an object, such as { size: "5xl", weight: "bold" }; has <field>, which text styles don't have; has <field> twice; has the <field> <value>; has no size)The fields a text style has, or the steps its field takes.
MK7005errorThe site theme changes what the class <class> means. Reported by the publisher, in checks, previews, and publishing, once per color or text style, at its name.Rename the color "<name>": UnoCSS already reads <class> as something else; or rename the text style, since text-<name> is one of UnoCSS's own classes.
MK7006warningThis <img> has no alt. At its tag. Not for one with alt="", an alt worked out by code, or role="presentation" or "none".Say what the image shows in alt, or write alt="" where it's only decoration, so screen readers skip it.
MK7007warning <hN> comes after <hM>, skipping a heading level. At the heading. Within each of a file's top-level declarations, in the order written: going up any number of levels is fine, and the first heading isn't judged, since the page may put it under others.Use <hM+1> here: headings go down a level at a time, and a heading's size is its text style's, not its level's.
MK7008warningThis <tag> has no label. An <input>, <select>, or <textarea> without aria-label, aria-labelledby, or title, a <label> around it, or an id that a <label for> in any of the site's files names. A placeholder isn't a label. Inputs of type hidden, submit, reset, button, and image aren't checked.Put it in a <label> with its words, give it an id that a <label for> names, or give it aria-label.
MK7009warningThis link has no words a screen reader can say. (or This button …) A link with an href, or a button, with no words, no alt of an image in it, no <title> of a drawing in it, and no aria-label, aria-labelledby, or title.Give it words, or aria-label saying what it does, such as aria-label="Open the menu".
MK7010warningThe link text "<words>" doesn't say where it goes. A link whose words are only "click", "click here", "here", "link", "this link", "more", "read more", or "learn more", in any case, ignoring one last punctuation mark.Say what it opens, such as "Read the pricing guide", or give it aria-label that does.

Internal errors#

MK9999 means the compiler itself failed. It's always a bug in Mockly, not in your site. Tell us what you were building when it happened.