Skip to content
weftGitHub

Tooling

Diagnostics

Every code weft reports, 272 in all. weft docs <code> prints the same page.

Compiler

CodeCatches
W000Syntax error; nothing else in the file is checked
W080A component with an untyped parameter that nothing renders: its props have nothing to be inferred from
W081An exported component with no written prop type
W082A generated /*inferred*/ prop type no longer matches the callers
W083An any weakens an inferred prop; names where the any came from
W084One caller passes a type the component body can't handle, and the others agree
W085An inferred prop names a type the component's scope can't see
W001A prop or .value used to compute something in the component body outside derived/watch/JSX/handlers (the body runs once). const x = props.y and destructuring only name the prop and stay live
W002Writing state inside derived
W003watch writes state that its own source reads (cycle)
W004watch that only copies one value into another
W005state(prop): React's useState(prop) bug, the box never follows the prop
W006Nested prop destructuring (({ user: { name } })) can't stay live
W007Assigning to a Derived (maps TS2540)
W008Box/value mismatch at a prop (a State<T> where T is expected, or the reverse)
W010.map over objects without key
W011A number on the left of && (renders 0)
W012derived(async …), or reads after await in resource (untracked)
W013Early return inside a JSX .map callback
W014Identity-breaking .map(p => ({ ...p, … })) feeding a keyed list: every row re-runs on every change (in-file, and across files via props in weft check)
W015A box rendered without .value in JSX (a child, or a native attribute)
W017One entry of a whole object read per row (cart[p.id], cart.value[p.id] in a keyed-list row or its props, box.value[prop] in a row component): every change re-runs every row
W018const x = untrack(() => …reactive…) in a component body that only the JSX reads: it renders the setup-time value and never updates
W019Unsupported row pattern
W020React-isms (W020–W029): useState, useEffect, useMemo, useRef, useCallback, custom useX() hooks, dependency arrays
W030Module-level state reachable from the server entry graph (leaks across requests)
W040Early return in a component body that depends on state (runs once, never re-checked)
W041let variable mutated in a handler and read in JSX (never updates)
W044A key read from crypto.randomUUID(), Date.now(), Math.random() or performance.now(): it differs between server and client, so the row is rebuilt on hydration and on every render. At runtime the same miss logs a key differs between server and client
W043data-x={true} or data-x={false} on a native element: data-* booleans are presence flags, so these write data-x="" or nothing, not React's "true" and "false"
W050Raw addEventListener, setInterval, new WebSocket or new EventSource in a component with no cleanup (it outlives the component)
W051A @weft-js/core/browser value, on or interval created outside an owner: module scope, a timer or promise callback, an event handler, after await
W052watch folding a stream by hand
W053.live() on a query with no tags (dev): no mutation can ever poke it
W054media(query) without ssr in a server-rendered component (hydration mismatch); a warning when weft check cannot tell
W056watch that copies a live value (a @weft-js/core/browser value, .live()) into state
W060A group() from @weft-js/core/style that can't be evaluated at build: a value that isn't a literal (named with its path), a group() outside a top-level const, or an accent that isn't literal tokens
W061bind:x on a native element, or bind:x/bind(box) with something that isn't a box or that isn't a component prop

Design

CodeCatches
D001A raw colour in style, app CSS, or a Tailwind arbitrary or palette class, in an app with warp's tokens: it won't follow the theme, the dials or dark mode
D002Spacing off the scale, or a scale step written by hand (padding: 16px, p-[13px], gap-3)
D003Raw type, radius or shadow (font-size, text-lg, rounded-[7px], box-shadow)
D004Layout spelled out on an element (display: flex, flex flex-col gap-4) where a primitive says it
D040The style gate (data-weft="styled") on the main content or a page heading: the first paint hides what it should show
D041aria-label or aria-labelledby on a <div>, <span> or other element whose role may not be named: screen readers drop it (axe aria-prohibited-attr)
D005A browser-painted control in a warp app: <button>, <select>, <textarea>, an input type like date, number or range, or a title tooltip
D006One heading level at two roles across an app (h1 as both title and display), or a bare <hN> beside <Heading>
D007A bare <a href> in a warp app
D011A literal text and background colour pair on one element below 4.5:1
D030An <input>, <select> or <textarea> with no label
D031A button or link with no accessible name (icon only, or empty)
D032<img> with no alt
D033onClick on a non-interactive element with no role: mouse only
D034A control inside a control (<a><button>)
D035A heading level that jumps (h1 to h3)
D036The focus ring removed with nothing in its place (outline: none, outline-none)
D037A positive tabindex
D038Two solid Buttons side by side
D039<html> with no lang
D020Class soup: a static class/className with more than 8 classes, or the same class list (4+ classes) in two or more places
D021Two style groups resolve to 90% or more of the same CSS (similar in .weft/styles.json)
D022The same accent (accent: { … } at a call site) on one group at 3 or more sites
D023A css: { … } escape (raw CSS) in a style group
D024Styles that fight with no order: two group roots spread on one element; two compounds that can match at once and set one property differently, or an accent that sets what the site's own axis value sets (weft check)
D025Unknown name in a style group: an axis value, axis, state or part a call site uses that the group doesn't have, or an intent or token that isn't in the vocabulary (also a type error)

Tests

CodeCatches
T001A test with no assertion (or an expect(x) with no matcher): it passes as long as nothing throws. @weft-js/core/test also fails such a test at runtime
T002Tautology: expect on a literal, or a value compared with itself
T003Asserting a value the test just set (n.value = 5; expect(n.value).toBe(5)) without going through the UI or the graph
T004Sleeps, real timers or waitFor polling in a test
T005Implementation query: container, CSS selectors, classes, innerHTML, test ids, or app state read directly in a component test
T006Mocking weft (vi.mock('@weft-js/core')) or the unit under test, so the test checks the mock
T007Snapshot without approval: it records today's output and passes against it
T008Conditional expect (if, ?:, &&, catch), or a test whose only expects are in a loop
T009.only or .skip committed (also fit, xit, xdescribe)
T010A new or changed test passes on the code before the change (fail-before), so it does not test the change
T011A new test kills none of the mutants of the changed lines: it would still pass if that code were wrong (a warning when the mutants were sampled)
T012A test ends with @weft-js/core/browser listeners, intervals or subscribed values still open (checked by @weft-js/core/test after each test)
T013A real WebSocket or EventSource in a fast test
T020An existing test deleted, skipped, marked .fails, or with an assertion removed or loosened (toBe(3) → toBeTruthy())

Plugins and config

CodeCatches
P001Stray top-level script file outside a declared task
P002package.json script that is not a weft … alias
P003Task duplicates a built-in command or another task (same name or same command)
P004Task without description or sideEffects
P005Write outside scope.write
P006Unreferenced one-off script
P007weft.config.ts fails to load or fails the schema (the Zod path is given)
P008slots names a slot id weft does not define
P009slots picks a winner that is not in plugins (or does not provide that slot)
P010More than one weft.config file
P100Two providers for a one slot (or the same key in a keyed slot)
P101A slot consumer is used but nothing provides it
P102Provider implements no slot interface version this weft supports (P102w: bound through an adapter)
P103Plugin's weft peer range doesn't include the installed weft
P104requires names a slot that nothing provides
P105Cycle in order.before/after
P106Two plugins' transform filters matched one module with no declared order
P107Unfiltered resolveId/load/transform, or a config hook
P108Plugin over its HMR budget share
P109Undeclared permission used, or permissions widened since the lock
P110Installed plugin integrity differs from .weft/plugins.lock.json
P111Diagnostic code outside the plugin's namespace, or a namespace collision
P112Plugin AGENTS section over 1 KB
P113Token, catalog, compiler-data or Vite-config key collision
P114Provider runtime code in an entry chunk (eager import)
P115Invalid plugin options (Zod path given)
P116Same plugin twice, or name/version does not match package.json
P117contribute() is not pure
P118Server code registered outside a server-side slot
P119Provider failed its slot's conformance suite
P120A weft plugin's vite part registered directly in vite.config.ts

Head and metadata

CodeCatches
H009A prop of an island is something JSON can't carry (a function, a Date, a class instance), found as the server renders
H010An island has to load with the page for a reason its code could avoid: work that reaches past it, a module that runs as it loads, a box at module level, code the compiler can't see into. .weft/hydration.json says why for every island
H001No site.url: a build has no absolute canonical, og:url or og:image, and a server takes them from the request's Host header
H002Param route with an auto image on a static-only preset and no prerender (weft routes --meta --preset static)
H003No description after merging the config, layouts and route (weft check, per route and on site)
H004Title over 60 characters or description over 160 (search results and cards cut them)
H011A page sets no meta, or leaves out its title or description: they come from what it renders (its h1, its first paragraph) or the site default
H012Two routes have the same title
H013Two routes have the same description, usually the site default
H014A param route whose meta ignores data and params, or whose real pages all have the same title or description
H015A written description that reads like code, a markdown fragment or a cut-off sentence
H016A route weft check couldn't fully check: it failed here, or a param route with no page to sample
H005.og.tsx uses a style or element the card renderer does not support (an error in dev; production drops the declaration)
H006meta reads a box or calls a hook (metadata is plain data); found statically by weft check
H007meta (or a card) reads a deferred (streamed) field of the loader's data: the request fails with 500 instead of a page with missing tags
H008A card's <img> didn't load (timeout, HTTP error, not an image, too large, host not allowed); the card renders without it

Queries

CodeCatches
Q001q() (a live query, a Resource) called with no owner: in load, a handler, a timer or module scope
Q002A query's or mutation's input failed its schema (400, with the issues and their paths)
Q003query/mutation defined outside a server module: its run and imports would ship to the client (the build fails)
Q004A mutation called while the server handles a GET (load, a page render, a query's run)
Q005A server function's result doesn't survive JSON (Date, Map, Set, a class, undefined in an array, a function, NaN): the client sees another value
Q006A cached (cache: { life }) query reads ctx.request, ctx.response or ctx.user: its value is shared by every user
Q007redirect() after the page started streaming (a component's query): the status is already sent
Q008A mutation invalidates a tag no query declares (todo:* covers todo:${id}): nothing refreshes
Q009/_weft/fn/<id> names no function: the page is older than the server, or the module isn't a server module
Q010A mutation request without the CSRF proof (POST, JSON, x-weft: 1, same origin): 403
Q011A function read or wrote a table its tags/invalidates don't name (dev): a mutation of that table won't refresh it
Q012@weft-js/start/db* or @weft-js/start/auth reached the client graph
Q013A database URL the driver can't open (a postgres:// URL given to the SQLite entry, or the reverse)
Q014The database doesn't match the schema: a table or column is missing
Q015src/db/schema.ts changed since the last weft db generate
Q016A request used the database while it was down, or a migration it needs failed (503; the server log has the driver's error). Pages that run no statements still serve

State

CodeCatches
S011mutation({ optimistic }) updated a query its invalidated tags didn't cover: the prediction has no fresh value to land on (held until a refetch)
S001A server object or array (q().value, await q.fetch(), or a map, filter or spread of one) copied into state: it goes stale and its rows lose their identity
S002A defineState scope reached on the server outside a render (module scope, load, a server function, a timer): it would be shared by every request
S012state.linked over a query value with no key: a refetch resets what someone is typing
S013A local or session persisted box read in a server render: the page shows init, then flashes to the stored value

Forms

CodeCatches
F001<Form of> was given something that isn't a mutation
F002A form POST without the same-origin proof (Origin, or Sec-Fetch-Site: same-origin): 403
F003A form POST naming no known mutation: the page is older than the server

Icons

CodeCatches
I001<Icon name="as">, or Icons.As, where no such icon exists
I002An icon name only known at runtime, so that use loads the pack
I003The icon pack used as a value, so all of it ships with the page

Auth

CodeCatches
A001The auth secret is missing or under 32 characters on a production server
A002A function with auth: 'user' was called with no session (401)
A003auth: 'user', but ctx has no user: the context file never asked for the session
A004A mutation changed the session cookie without a redirect: the tab keeps what the last session cached
A005The database provider has no Better Auth adapter

Contracts

CodeCatches
C001A function's input or output schema can't become JSON Schema: the contract says {}
C002A function's value failed its output schema (dev and tests)

Realtime

CodeCatches
R001channel or sharedState without authorize (weft check, weft build, and thrown at definition)
R002The memory bus on a multi-instance preset, or durableObjectBus outside cloudflare_durable (weft build fails): a message reaches one instance's clients
R003A mutation wrote a table a live query reads, and their tags don't intersect (dev): open pages won't update
R004A wildcard in realtime({ origins }), or a hand-written WebSocket handler (crossws, defineWebSocketHandler) that skips the Origin check: any site could join with a visitor's cookies
R005A stream's input, a channel's params or client messages, or a shared key or value has an unbounded string or array (weft build): a client decides how much the server holds
R006A stream's run read the database before it subscribed to a topic (dev): what's published in between is lost
R007ws, socket.io-client, reconnecting-websocket or @microsoft/fetch-event-source in dependencies: weft owns the connection
R008A message over 1 MiB, a request or socket frame over 64 KiB, or a client over the channel rate limit (20 messages a second)
R009Over 100 subscriptions or joins on a connection, or 10 connections or sockets for a user
R010An origin that isn't the app's own or in realtime({ origins }) tried to open a connection
R011A channel's authorize refused a join or a write (the status is unauthorized), at the join or a session re-check
R012A live query reads a table changes reports and no tag names it, or a table changes doesn't list (dev): writes from outside the app won't refresh it
R013A query marked live: { required: true } on a target that can't deliver a change feed: a functions preset with no external listener, or no changes at all (weft check, weft build, weft doctor)
R014changes on a preset with no long-running process: writes from outside the app won't reach .live(), which shows degraded
R015A required live query, and the server's startup probe found no real change feed (a pooler, no trigger, no trigger permission, a Mongo without a replica set): the server stops rather than serve stale data, and dev answers 503
R016The server settled on a degraded live mode (poll or off), or a working feed went down: .live() shows degraded or reconnecting
R017dbChanges on a client it can't watch or something that isn't a table, or weft db listen on the memory bus, which stops at that process
R019A query's live: { verify } round trip failed against its target (weft check, weft build): the message names the link that broke, from trigger to cleanup
R020A connection string with a password in source, in a verify or a target: it would be committed
R021A verify target isn't available here: fails weft build, and weft check warns and skips
R022A verify target that looks like production, or that nothing marks as not: a round trip writes rows
R023A verify passed with only one kind of target, so some links went untested
R024A channel or sharedState built for a target that can't hold a WebSocket (Netlify, Vercel, AWS Lambda): weft build fails, and where it is let through the page never dials, the channel's status is unavailable and a send rejects with R024
R018A table in changes that no migration gives a change trigger (weft check, weft build): the server would fall back to polling
R101A function with impl has no output, a type JSON can't carry (z.date(), z.bigint(), z.void(), z.custom()), or a one-way .transform() on its input (weft build, weft check, weft dev)
R102A plain z.union of objects in a function with impl: it has no tag, so Rust can't tell its arms apart reliably. Scalar unions and .nullable() are fine
R103A sidecar doesn't match the contract: cargo stops where a generated type or method no longer fits (with the function's id), a binary built from another contract, an output the output schema rejects, or input serde refuses
R104native() on a preset that can't run it (no file system for an addon or a .wasm), on a preset with no WASM when wasm: false is set, or a .wasm that won't build (a warning on a preset the addon runs on). At run time, a build with neither for this platform (503)
R105A cached Rust query with tags that reports no table (weft check reads the method; weft dev sees the call): Q011 can't see into Rust, so nothing checks that the tags cover what it reads
R106A remote() call can't be made: no address, no WEFT_RUST_SECRET, or the sidecar refused the signature (503)
R107A sidecar didn't answer, or ran past the deadline (503, 504). Only that function fails
R108A sidecar function panicked or returned an undeclared error (500). The detail stays in the sidecar's log, under the request id
R109A sidecar doesn't build for a reason no contract function explains: a compile error, no crate, or no cargo

Surfaces and fonts

CodeCatches
X001s.text() predicted one height and its DOM mirror renders another: font, lineHeight or width differs from the CSS
X002system-ui, -apple-system or a generic-only family on a measured s.text(): canvas and CSS can resolve it differently (also checked in dev, for fonts that come from CSS)
X003s.text() prepared while its font isn't loaded and no "<Family> Fallback" face exists: it's measured on another font until the swap
X004A surface text mirror uses a style line breaking can't predict: text-wrap: balance|pretty, hyphens: auto, word-spacing, letter-spacing, font-feature-settings, font-variant-numeric, font-variation-settings
X005font-size in vw/vi/cqi/cqw with no rem term: zoom and the user's font size don't change it (WCAG 1.4.4)
X006A fluid clamp() font size whose max/min is over 2.5: 200% zoom no longer doubles it
X008Viewport meta with user-scalable=no or maximum-scale under 2: blocks pinch zoom
X009<Surface> with neither label nor decorative: a screen reader finds an unnamed image
X010fillText or strokeText in a surface: text only the canvas knows, with no selection, find, translation or screen reader
X011A surface text mirror hidden from assistive tech: aria-hidden on <Surface> or s.el, or display: none/visibility: hidden on [data-surface-text] (also checked in dev)
X012CSS that defeats a surface's text fallback: forced-color-adjust: none, or a print rule hiding [data-surface-text]
X020A hand-written @font-face for a local file with no "<Family> Fallback" face: the font swap shifts layout
X021font-display: block or auto: text is invisible while the font loads
X022A local @font-face without unicode-range (not subset), or the font build's preloads over the 100 KB budget
X023A <link rel="preload" as="font"> whose file no @font-face uses
X030<img>, <video> or <iframe> without width/height or aspect-ratio inside a <Boundary>: it lands at 0 px and pushes content down
X031A pending fallback whose box differs from the content's by more than 1 px at the swap (measured by <Boundary> in dev)
X032A skeleton repeats a list with no count hint: no limit input, no .max() on the array, nothing seen yet. It shows 5 rows, so the swap can shift
X033x.pending renders a skeleton or spinner with no check that x has no value: every refetch flashes it
X034A pending fallback with its own setTimeout/setInterval: <Boundary> already owns the 150 ms / 400 ms timing
X040Per-grapheme DOM spans over a string known to exceed 200 graphemes
X041Per-character spans without aria-hidden, or a parent with no accessible name: a screen reader spells the word out
X035Streamed text written straight into a scroll container: a reflowing tail moves the lines above it
X036A skeleton shows a string from output with no length hint, so its bar is a guess
X037A resource or query made in the component that renders the <Boundary> meant to wait for it, and read inside it: the Boundary only waits for what its children make, so no fallback shows

Media

CodeCatches
M001Raw <img>/<video> without width/height or aspect-ratio, outside a <Boundary> (inside it is X030)
M002An image rendered through a ?url import or a string path, bypassing the optimizer
M003A raw <img> whose file is wider than its width × 2 (DPR) × 1.25: bytes nobody sees
M004A GIF over 256 KB rendered raw
M005<video autoplay> without muted: browsers block it, and sound before a gesture is hostile
M006<video> without poster: a blank box until the first frame decodes
M007Missing alt on <Media>/<img>, <Media alt=""> without decorative, or alt equal to the file name
M008A route's first media is loading="lazy", or priority media is lazy: the likely LCP image loads late
M009priority on more than 2 media in one route: they compete for the first bytes
M010A priority <Media> without width or sizes: eager images can't use sizes="auto", so the browser assumes 100vw
M011<video autoplay loop> without controls, or a GIF in a raw <img>: endless motion with no pause (WCAG 2.2.2)
M012An SVG with <script>, foreignObject, on*= handlers or external references, used as an image
M013A file in public/ referenced by string: untracked, unoptimised, no dimensions
M014What encoder: 'compatible' adds to the build for compatible copies
M015An HDR or over-8-bit video that comes out 8-bit SDR
M016<Media src> given a string: nothing knows its size before it loads, so the page shifts
M017ctx.media or a /_weft/m/ URL used with no media.server provider
M019ffmpeg did not run, is not the pinned binary, or has no encoder for a codec
M018An upload refused: it isn't an image weft can read, or it's over the size limit (422)