Documentation menu

Error codes

Development builds of @gyral/core print each diagnostic in full, ending with its code and a link to this page: … (G0010: https://gyral.dev/errors/#G0010). Production builds print a short line instead, so the message texts stay out of your bundle: Gyral, the code, the message’s arguments in order, and the link.

Gyral G0010 my-cart Add https://gyral.dev/errors/#G0010

Read that one as: <my-cart> has no update for the message Add. Find the code below; its message shows where each argument goes. For the full sentence, reproduce the problem in development (the dev server, tests, or a build with --mode development). A hydration mismatch keeps its own sentence in production and ends with its code and link.

The codes and messages are read from @gyral/core 0.3.9, the version this site is built with; codes are never reused.

Components

G0001

<{tag}> is a browser element (render it on the server with @gyral/ssr).

Arguments: tag.

Outside the browser, define() only records your component for the server renderer, so there is no element class to construct: new MyElement() on the server throws this.

Fix: Render the component on the server through @gyral/ssr (renderPage, renderToString), instead of constructing it.

G0010

<{tag}> has no update for message "{message}".

Arguments: tag, message.

A component received a message whose _tag has no reducer in its update, so the message was dropped. It is usually a typo, or a reducer you forgot.

Fix: Add a reducer for the message to the component’s update, or stop sending it.

G0011

<{tag}> has no intent parser for data-intent="{intent}".

Arguments: tag, intent.

An event reached an element whose data-intent names an intent the component has no parser for, so the user’s action was ignored.

Fix: Add a parser with that name to the spec’s intent, or correct the name in the view (data-intent=${i.Name} catches typos at compile time).

G0012

<{tag}> intent parser for "{intent}" failed

Arguments: tag, intent.

An asynchronous intent parser rejected. The error is logged with this message, and the event produced no message.

Fix: Handle failures inside the parser (return a message that describes the failure, or undefined), or move the fallible work into a command with onFailure.

G0013

<{tag}> focus("{selector}") matched no focusable element.

Arguments: tag, selector.

A focus(selector) command found no element to focus in the component’s root after the render, so focus stayed where it was. Keyboard and screen reader users notice.

Fix: Make the selector match an element inside the component (its shadow root, or its children for shadow: false). Give targets that aren’t focusable, such as headings, tabindex="-1".

G0014

<{tag}> is on the page, but no custom element named "{tag}" is defined {seconds} s after it loaded, so it renders nothing. If it is one of your components, import the module that calls define()('{tag}', …) from the entry; for a third-party element, load its package. Ignore this for an element defined later on purpose.

Arguments: tag, seconds.

Development builds look for custom-element tags on the page that nothing has defined, 3 seconds after load and again after each later define(). Such an element renders nothing: usually the entry never imports the module that defines it.

Fix: Import the component’s module from the entry (or from a module the entry imports). For a third-party element, load its package; ignore the warning for an element you define later on purpose.

G0015

Two copies of @gyral/core run in this page: {first} from {firstUrl}, and {second} from {secondUrl}. A value one copy makes, such as a stylesheet from css or a store, does not work in the other. Install one copy: pin @gyral/core to one version with your package manager's overrides (gyral() warns when it is installed more than once).

Arguments: first, firstUrl, second, secondUrl.

Development only: two copies of @gyral/core run in the page. A value one copy makes, such as a stylesheet from css or a store, doesn’t work in the other, so components fail far from the cause (styles.flatMap is not a function). gyral() also warns at startup, in dev and builds, when the package is installed more than once, naming each copy’s version, folder and the packages that use it.

Fix: Give every @gyral/* package the same version: they take @gyral/core as a peer, so they share the app’s copy. For a library that pins another core, pin @gyral/core to one version with your package manager’s overrides (pnpm or npm overrides, Yarn resolutions) and reinstall. A library lists @gyral/core as a peer dependency (Publishing a component library).

G0016

{package} {version} needs @gyral/core {version}, but this app has @gyral/core {core}. Gyral's packages are released together: install the same version of every @gyral package.

Arguments: package, version, core.

A Gyral package (@gyral/http, router, time, ssr, testing or devtools) loaded with a @gyral/core of another release. Each one uses core’s internals, which change between releases, so it checks core’s version when it loads. They take @gyral/core as a peer at their exact version, so the package manager normally reports the mismatch when it installs: pnpm lists the unmet peer, npm stops with ERESOLVE. Development throws, naming both versions; production logs this warning and carries on.

Fix: Install the same version of every @gyral/* package the app uses, and check that the lockfile resolves one version of each (One version of every package).

G0017

<{tag}> rendered differently with the same state, props and stores, so its view reads something else, such as Date.now(), a module variable or the DOM, or builds a value anew each render that is compared by reference (a Date, a Set, a class instance). It differs {where}. A component renders only when one of them changes (0.3.8): move what the view reads into state, a prop or a store.

Arguments: tag, where.

Development only: a component renders only when its state, a declared prop or a declared store’s value changes, and this one rendered differently when Gyral rendered a skipped view again to check it. So its view reads something else, such as Date.now(), Math.random(), a module variable or the DOM, and on the page it shows a stale value until one of its inputs changes. The check compares what the view renders, plain objects and arrays by value, so values a view builds anew each render match when they are equal; a Date, a Set or a class instance built in the view compares by reference, so it differs every time. where says where the view differs: in its template, or in the innermost each(…, Row) or keyed(…) that does, named by its row function. When that list’s pick returned a new object for the same item, the message says so. Development checks up to 50 skipped views per render pass (a different 50 each pass when more are skipped, so every component is checked within a few passes) and warns once per component.

Fix: Move what the view reads into state (the time from an every subscription, a random value from a command, a Date built from a timestamp), a prop or a store, so that a change to it renders the component (Pure means repeatable). When the message names a list’s pick, pick only the fields the row reads, or pass each(items, key, row, { pick, equals }).

G0018

The parser of intent "{intent}" is on("{expected}"), but a "{actual}" event reached it. Bind the intent to that event (data-intent-{expected}=${…}), or give it a parser of its own.

Arguments: intent, expected, actual.

The intent’s parser is on(event, …), written for one event type, but another event reached it: the template binds the intent to a different event, through the element’s own trigger, data-intent-on or another data-intent-<event> attribute. Development throws; production declines the event, as a parser that returns undefined does. The gyral/intent-event ESLint rule reports the same binding in the editor.

Fix: Bind the intent to the parser’s event, data-intent-keydown=${i.Move} for on("keydown", …), or give the other event an intent and a parser of its own (Parsers typed by their event).

Props

G0020

<{tag}> prop "{prop}" has an asynchronous schema; it must be synchronous.

Arguments: tag, prop.

A prop’s Standard Schema returned a promise. Props are validated synchronously, while the element updates and renders, so they can’t wait for it.

Fix: Use a synchronous schema or a type guard for the prop, and do asynchronous checks in a command.

G0021

<{tag}> prop "{prop}" got an invalid value from {source}, so it is treated as missing: {issues}

Arguments: tag, prop, source, issues.

A prop value failed its type or its schema, so it is treated as missing and its default applies. Attributes are checked in every build; property sets and hydration seeds in development only.

Fix: Pass a value that matches the prop’s declaration (a numeric string for prop.number, valid JSON for prop.json), or loosen the schema.

G0022

<{tag}> is missing required prop(s): {props}.

Arguments: tag, props.

A component rendered for the first time while props declared required were still missing. The parent probably forgot an attribute or a property.

Fix: Set the prop on the element (an attribute, or .prop=${…}) before it renders, or give it a default instead of required.

G0023

<{tag}> declares prop(s) named like a member every element already has: {props}. The prop would replace it: a prop named "hidden" hides the element, one named "state" hides the component's model. Rename the property. The name of one of Gyral's members (state, send, drivers, stores) can stay the attribute: badge: prop.string({ attribute: "state" }).

Arguments: tag, props.

A component declares props named like a member every element already has: a built-in property (hidden, title, id), or one of Gyral’s (state, send, drivers, stores, initialMessages, the custom-element callbacks). The prop would replace the member: a prop named hidden hides the element, one named state hides the component’s model. TypeScript rejects these names and development throws; production warns about built-in names.

Fix: Rename the property. One of Gyral’s names can stay the public attribute: badge: prop.string({ attribute: "state" }).

G0024

<{tag}> has a "{attribute}" attribute, which no prop reads: HTML lowercases attribute names, and prop {prop} reads "{expected}". Write {expected}="…", or set the property (el.{prop} = …).

Arguments: tag, attribute, prop, expected.

Development only: the element carries an attribute HTML lowercased from a camelCase prop’s name, such as playerbase for playerBase, written in the page’s HTML or set by a host framework. The prop reads its kebab-case attribute, player-base, so the value never reached it.

Fix: Write the prop’s attribute (player-base="…"), or set the property in code. In templates, the build and ESLint report the same mistake (template rules).

Scheduling

G0030

gyral: a deferred callback failed

Arguments: none.

A callback Gyral deferred to a microtask threw: delivering a child component’s output to its parent. The parent may not have received the output.

Fix: Look at the error logged with this message, and make sure the parent’s child() mapper and any gyral-output listener don’t throw.

G0031

<{tag}> view failed; it shows its error view, or its previous DOM stays.

Arguments: tag.

A component’s view threw during a render. Gyral reports a GyralError (phase view) through its error channel and keeps rendering the other components. The component renders its error view if its spec has one; otherwise its DOM shows its previous state.

Fix: Fix the exception in the view, using the cause of the reported error. It is often a field read on state that has a different shape than the view expects. Give the spec an error(failure, state) view so users see a fallback (Error handling).

G0032

gyral: post-render work failed

Arguments: none.

Work that runs after a render failed: a focus command, custom states from states, the Hydrated message, or the commands init returned for a resumed component.

Fix: Fix the exception logged with this message, typically in the spec’s states function or the Hydrated reducer.

G0033

gyral: rendering did not settle in one flush (a cycle between {tags}). Components are probably feeding each other props or messages; break the cycle with a condition in update.

Arguments: tags.

Rendering didn’t settle in one flush: the named components kept re-rendering each other. Components are probably feeding each other props or messages in a cycle. Development throws; production logs it and drops the rest of the work so the page doesn’t freeze.

Fix: Break the cycle with a condition in update, for example by sending a message or changing state only when the value actually differs.

G0034

settled(): the page did not settle after {rounds} flushes or busy turns. A stream is emitting without pause, or components (or drivers) are feeding each other messages in a cycle.

Arguments: rounds.

settled() rejected because messages kept arriving: more than the given number of flushes or busy turns went by without a quiet moment. Something is sending messages in a cycle, or a stream emits on every turn.

Fix: Find the cycle and break it with a condition in update, or substitute the chatty driver in the test.

Commands

G0040

{owner}: the onSuccess or onFailure mapper for driver "{driver}" threw; no message was sent.

Arguments: owner, driver.

A command’s onSuccess or onFailure mapper threw while turning a driver’s result into a message, so the result never reached update. It is reported as a GyralError (phase command), and the component’s Errored reducer, if any, receives it.

Fix: Make the mapper total: return a message, or undefined, for every input.

G0041

{owner}: driver "{driver}" failed and the command has no onFailure.

Arguments: owner, driver.

A driver failed, after any retry() policy, and the command has no onFailure. This is an error, not a warning: it is reported as a GyralError (phase command) and reaches the component’s Errored reducer, if any. Without one, the component can’t react: a spinner may spin forever.

Fix: Give the command an onFailure that turns the error into a message, and handle that message in update. Expected failures (the network, a 5xx, a refusal) should always be messages.

G0042

gyral: subscription "{name}" failed to unsubscribe

Arguments: name.

The function (or unsubscribe()) a subscription() returned threw when Gyral released the source, so the source may still be sending values or holding listeners.

Fix: Make the stop function that subscribe returns safe to call once, and return a function or an object with unsubscribe().

G0043

{owner}: no driver provides "{driver}". It is a driver token: provide its implementation with provideDrivers(root, [token.provide(run)]) or el.drivers. A page provides it before its components' first render: before importing them, or later in the same task (no await in between).

Arguments: owner, driver.

The command’s driver is a token: defineDriver<I, O, E>(name) without an implementation of its own. Nothing above the component provides that name, so the command can’t run. It is reported through the error channel (phase command); onFailure isn’t called, because there was no driver to fail.

Fix: Provide the driver where the app starts, provideDrivers(document.body, [token.provide(run)]), or on the element with el.drivers. A page provides it before its components first render: before importing them, or later in the same task with no await in between (Substituting drivers). In tests, pass a fake of the token, fakeDriver(token), which takes its name (Drivers the app provides).

G0044

Two drivers named "{name}" in one list of drivers: give each a name of its own, or drop one.

Arguments: name.

Development only: a list of drivers (for provideDrivers, el.drivers, <gyral-drivers>, mount({ drivers }) or a store) has two drivers with the same name. A command finds its driver by name, so only one of them could ever run. Development throws; production has no check and uses the first.

Fix: Drop one of them, or give each driver a name of its own and build each command from the driver it should use.

G0045

A driver supplied as "{key}" is named "{name}", so commands of the "{name}" driver never use it. Supply drivers as a list, each under its own name (provideDrivers(root, [driver])), or make the key match.

Arguments: key, name.

Development only: a record of drivers has a key that isn’t the name of the driver under it, such as { api: makeHttpDriver() } for a driver named http. A command looks its driver up by the driver’s own name, http here, so that entry is never used: the lookup goes on to the next provider and, last, to the command’s own driver (for a token, G0043). Development warns once per record.

Fix: Supply the drivers as a list, each under its own name, provideDrivers(root, [makeHttpDriver()]), or make the key the driver’s name (Substituting drivers).

Stores

G0050

<{tag}> uses store "{store}" without declaring it. Add it to the spec: stores: [{store}], so the component subscribes to its changes.

Arguments: tag, store.

A component read a store with ctx.read(store), or sent to one, without listing it in its spec. A component subscribes only to the stores it declares, so it would miss the store’s changes.

Fix: Declare the store in the spec, for example stores: [cart].

G0051

store "{store}": the server's seed failed its schema, so it starts from init. Issues: {issues}

Arguments: store, issues.

The store state the server seeded into the page failed the store’s schema, so the browser discarded it and the store starts from init. The page may show different data than the server rendered.

Fix: Fix the state the server sends, or align the store’s schema with it.

G0052

unreadable {attribute} seed on {where}

Arguments: attribute, where.

The store seed on the page, or on a <gyral-stores> provider, isn’t valid JSON, so every store in it starts from init.

Fix: Let @gyral/ssr write the seed (renderPage({ stores }), or the provider’s .instances) instead of writing it by hand, and check that nothing rewrites or truncates the HTML on its way.

G0053

<{tag}> reads store "{store}" during a server render without a store scope. Pass the request's store instances to renderPage({ stores }) or renderToString(value, { stores }).

Arguments: tag, store.

A component read a store during a server render without a store scope. On the server each request needs its own store instances, and without a scope Gyral can’t tell whose state to read.

Fix: Render through @gyral/ssr and pass the request’s instances: renderPage({ stores }) or renderToString(value, { stores }).

G0054

store "{store}" sends to "{target}" but is not held by a store scope (a registry, <gyral-stores> or the page default), so the message is dropped.

Arguments: store, target.

A store’s update sent a message to another store, but this store instance isn’t held by a store scope, so the message has nowhere to go and is dropped.

Fix: Let a scope hold the instance: the page’s default, a <gyral-stores> provider (.instances=${[…]}), or a store registry in tests.

G0055

store "{store}" has no update for message "{message}".

Arguments: store, message.

A store received a message whose _tag has no reducer in its update, so the message was dropped and the change you expected didn’t happen.

Fix: Add a reducer for the message to the store’s update, or correct the tag being sent.

Hydration

G0060

<{tag}> has an unreadable {attribute}

Arguments: tag, attribute.

A server-rendered component’s data-gyral-seed isn’t valid JSON, so the browser can’t resume the server’s state: the component starts from init, and its first render may not match the server’s HTML.

Fix: Don’t edit or write data-gyral-seed yourself. Look for whatever changes the server’s HTML on its way: a proxy, an HTML minifier, string templating.

G0061

gyral: hydration code failed to load; rendering fresh

Arguments: none.

The hydration code, which loads lazily with the first server-rendered component, failed to load, so server-rendered components render fresh instead of hydrating. The page works, but redoes the server’s work. It is usually a stale deploy: a cached page that points at a chunk that no longer exists.

Fix: Keep the previous build’s assets online for a while after a deploy, or don’t cache HTML that points at removed chunks. The network error is logged with this message.

G0062

gyral: hydration mismatch in {host} at {path}: expected {expected}, found {found}. The server's HTML differs from the first client render: a view that isn't deterministic (Date.now(), locale), a third party that changed the DOM before scripts ran, or a stale cached page (https://gyral.dev/docs/server-rendering/#mismatches).

Arguments: host, path, expected, found.

The server’s HTML differs from the first client render, so the component can’t be hydrated in place. Development throws HydrationMismatch, naming the template’s file and line; production keeps the sentence (the component, the DOM path, what was expected and found) and renders the component fresh (G0063). Both end the sentence with this code and its link.

Fix: Make the view render the same on both sides: no Date.now(), random numbers or locale-dependent formatting in view (put them in state through a command). Also rule out a browser extension or script that changed the DOM before hydration, and a stale cached page.

G0063

<{tag}> was rendered fresh after a hydration mismatch.

Arguments: tag.

Production only: after a hydration mismatch, Gyral cleared this component and rendered it fresh. The warning prints this code with the component’s tag, then the mismatch’s own sentence, which ends with G0062. The rest of the page stays hydrated, but this component flashes and redoes the server’s work.

Fix: Fix the mismatch the warning describes; reproducing it in development gives the full message and the template’s location.

Templates

G0069

<{element}> ?{binding} does nothing: this build left the dialog and popover code out, because no module the build scanned binds it (a package that does not depend on Gyral is not scanned).

Arguments: element, binding.

Production only: a ?modal or ?popover-open binding ran in a build that left the dialog and popover code out, so the dialog didn’t open. The build adds that code when a module it reads binds one, in source or in a package’s compiled templates, and it doesn’t read installed packages that don’t depend on Gyral.

Fix: Make the package that ships the template depend on @gyral/core (a library lists it as a peer dependency), so the build reads it, or move the template into your own source.

G0070

gyral: an html template was not compiled: `{start}`. Under the gyral-compiled condition every html`…` must be compiled by the Gyral Vite preset.

Arguments: start.

A bundle built with the Vite preset leaves out the runtime template preparer, so every html template must be precompiled, and this one wasn’t. The build normally fails before this can happen.

Fix: Import html (and svg) from @gyral/core, or from a package listed in the compiler’s sources, and use it directly as a tag: the compiler can’t follow an alias, a call or a re-export.

G0071

gyral: template rule 11: a page shell rendered in the browser.

Arguments: none.

A page shell, a template with <!doctype>, <html>, <head> or <body>, was rendered in the browser. Only the server renders document-level tags. Development reports it as template rule 11, with the full explanation.

Fix: Render the page shell on the server with @gyral/ssr’s renderPage, and render only components in the browser.

G0072

gyral: this page's Trusted Types policy list does not allow the policy "gyral", so templates are parsed from plain strings, which works only while Trusted Types are not enforced. Add `trusted-types gyral` to the Content-Security-Policy.

Arguments: none.

Gyral parses template HTML and raw() markup through a Trusted Types policy named gyral. The page’s Content-Security-Policy has a trusted-types directive that lists allowed policy names without gyral, so the browser refused to create it. Gyral falls back to plain strings, which works only while the page doesn’t enforce Trusted Types (require-trusted-types-for 'script'). Development warns once.

Fix: Add gyral to the policy list: trusted-types gyral (with your own policies, if any). If you don’t use Trusted Types, remove the trusted-types directive.

Error handling

G0073

<{tag}> update for "{message}" failed; the state is unchanged.

Arguments: tag, message.

A reducer threw while handling the named message. Nothing changed: the state stays as it was and the commands that reducer would have returned don’t run. It is reported as a GyralError (phase update), and the component’s Errored reducer, if any, receives it.

Fix: Fix the exception in that reducer, using the cause of the reported error. Add an Errored reducer if the component should show an error state (Error handling).

G0074

<{tag}> init failed; it shows its error view, or stays empty.

Arguments: tag.

A component’s init threw, so it has no state. It renders its error(failure, undefined) view if its spec has one, or stays empty. The error is reported as a GyralError (phase init).

Fix: Fix the exception in init, using the cause of the reported error; often a prop or seed read with an unexpected shape. Give the spec an error view for a visible fallback.

G0075

<{tag}> element hook failed; the other hooks still ran.

Arguments: tag.

An element hook’s client function threw. Gyral reported it (phase hook) and still ran the other hooks of that render; the element itself is in the DOM.

Fix: Fix the exception in the hook, using the cause of the reported error.

G0076

store "{store}": update for "{message}" failed; the state is unchanged.

Arguments: store, message.

A store’s reducer threw while handling the named message. The store’s state is unchanged and its commands don’t run. The error is reported as a GyralError (phase store); the sender isn’t affected.

Fix: Fix the exception in that store reducer, using the cause of the reported error.

G0077

store "{store}": a subscriber failed; the other subscribers were still notified.

Arguments: store.

A component that reads the store threw while handling StoreChanged. The other subscribers were still notified and the store’s commands still ran.

Fix: Fix the exception in that component’s StoreChanged reducer (or the code it calls), using the cause of the reported error.

G0078

<{tag}> failed to render on the server; it renders its error view (or nothing) and starts fresh in the browser.

Arguments: tag.

A component’s init or view threw during server rendering. It rendered its error view (or nothing), marked data-gyral-error and without a seed, and the rest of the page was sent; the browser starts that component fresh. renderPage’s onError received the error.

Fix: Fix the exception, using the error your onError received. Pass onError: 'throw' to renderPage if a failing component should fail the whole page instead, so the route can answer with its error page.

Subscriptions

G0079

<{tag}> subscriptions returned two commands for lane "{lane}"; only the first runs. Give each its own `key`.

Arguments: tag, lane.

The component’s subscriptions returned two commands in the same lane: the same key, or no key and the same driver. Gyral keeps one running command per lane, so only the first runs. Development warns.

Fix: Give each subscription its own key, such as one per room or per URL.

G0080

<{tag}> subscriptions kept restarting: a subscription's input data changes with every message it sends (for example a timestamp or a counter in the input). Keep inputs to what should restart it; functions are ignored, so to restart on a new selector, change the key.

Arguments: tag.

A subscription restarted again and again: its input changed with every message it sent, so each new state asked for a different input, which restarted it. Gyral stops after 100 rounds rather than loop.

Fix: Keep a subscription’s input to what should restart it (an id, a URL), not values that its own messages change, such as a counter or a timestamp.

Element calls

G0085

<{tag}> call("{selector}", "{method}") failed

Arguments: tag, selector, method.

A call(selector, method, …args) command failed after the render: no element of the component matched the selector, the element has no such method, or the method threw or rejected. The reported error’s cause says which. It is reported through the error channel (phase command).

Fix: Check that the element is rendered when the command runs (return the command from the reducer whose new state renders it), that the selector matches inside the component’s own tree, and that the method exists on that element.

G0086

call("{selector}", "{method}"): arguments must be plain data.

Arguments: selector, method.

Development checks that a call() command’s arguments are plain data that can be cloned (strings, numbers, arrays and plain objects). A function, an element or a class instance would make the command impossible to compare or record in a test.

Fix: Pass plain data, and let the element look up anything else itself.