Commit Graph

66 Commits

Author SHA1 Message Date
Niklas Mischkulnig bdbf63aef7 fix(next-custom-transforms): never place generated imports before directives (#98717)
## Summary

**What?** Fixes the error reported for a module that has a top-level
`"use client"` directive and an inline `"use server"` function
directive, e.g. `<form action={async () => { "use server" }} />` in a
client page.

**Why?** Under Turbopack this reported a bogus error — `The "use client"
directive must be placed before other expressions` — even though the
directive was already at the top of the file. The actual mistake (an
inline server action inside a Client Component) was never surfaced.

**How?** On Turbopack's RSC layer the server actions transform runs
*before* the React Server Components assert. When it hoists an inline
action it prepended its generated imports (`registerServerReference`,
action encryption, cache runtime) at index 0 of the module — *above* the
`"use client"` directive, which the transform does not consume. The RSC
assert then saw an import before the directive and reported it as
misplaced.

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
2026-09-16 17:09:32 +02:00
Janka Uryga 55b26f96ab [PPF] Scaffold unstable_prefetch() (#97618)
Scaffolds a new API, `unstable_prefetch()`. It's only allowed in server
code, so we ban it from being imported in the client.
Implementation follows upstack. Intended to be merged together.
2026-08-21 13:50:23 +02:00
Janka Uryga 2cc3694fda [PPF] Scaffold unstable_navigation() (#97236)
Scaffolds a new API, `unstable_navigation()`. It's only allowed in
server code, so we ban it from being imported in the client.
Implementation follows upstack. Intended to be merged together.
2026-08-20 15:27:11 +02:00
Hendrik Liebau cdf5c37d84 Scope app-entry export validation to files inside the app directory (#97350)
Adopts #97150. Closes #97150.

Fixes #96859

### What?

Since 16.3.0, builds fail for pages-router files named `sitemap` or
`robots` that export `getStaticProps`/`getServerSideProps`:

```
Error: "getStaticProps" is not supported in app/.
```

This is a regression from #94962, which added the metadata conventions
(`sitemap`, `robots`, `manifest`, `icon`, …) to the app-entry filename
regex in `ReactServerComponentValidator::assert_invalid_api`. The regex
only looks at the filename, so a file like `pages/sitemap.js` is now
mistaken for an app entry and rejected for exporting `getStaticProps`.

### How?

A file is only treated as an app entry when it's inside `appDir`,
reusing the gate `assert_server_filename` already applies to `error.js`.
The pages compilation context has no `appDir`, so pages-router files are
never validated as app entries.

On the test side:

- The fixtures that exercise the app-entry checks moved under
`app-dir/`, since the test harness derives `appDir` from the fixture
path. Their contents are unchanged.
- A new fixture (plus a `sitemap.js` fixture glob) asserts that a
pages-style `sitemap.js` compiles without errors.
- A new e2e suite, `test/e2e/pages-metadata-filenames`, covers
`pages/sitemap.js` with `getStaticProps` and `pages/robots.js` with
`getServerSideProps`. It fails without the fix under Turbopack (build
and dev) and passes with it under both Turbopack and webpack.

Supersedes #96873 (same approach, closed by its author) and closes
#96967.

---------

Co-authored-by: Rodrigo Arias <rodrigo@arias.me>
2026-08-14 11:53:24 +02:00
Hendrik Liebau c0c64a0ab1 Allow literal exports in 'use cache' files (#97181) 2026-08-12 06:42:24 +02:00
Josh Story d424aa8ec4 Stabilize unstable_instant (#94578)
This API has experimental modes for build validation but the dev
validation is going to be stable in the next release.
2026-06-09 13:50:25 -07:00
Aurora Scharff 108fe00fbd fix: replace docs/canary links with stable docs paths in error messages (#92528)
### What?

Error messages for the `"use cache"` directive and `after()` API (in
`cookies()`, `headers()`, `connection()`) pointed to `/docs/canary/...`
instead of `/docs/...`.

### Why?

On canary builds, `docs/canary` links can create circular or confusing
references. These APIs are stable and their docs live at the standard
`/docs/app/...` path.

### How?

Replaced all `nextjs.org/docs/canary/` URLs with `nextjs.org/docs/` in:

- `crates/next-custom-transforms/src/transforms/server_actions.rs` — the
`"use cache"` feature flag error
-
`crates/next-custom-transforms/tests/errors/use-cache-not-allowed/1/output.stderr`
and `2/output.stderr` — matching test fixtures
- `packages/next/src/server/request/cookies.ts`, `headers.ts`,
`connection.ts` — `after()` errors
- `packages/next/errors.json` — compiled error message index (6
occurrences)

<!-- NEXT_JS_LLM_PR -->
2026-04-08 20:28:46 +02:00
Jiwon Choi 66478522a8 Improve RSC error messages (#90702)
Improve the RSC error messages to be more comprehensive.

x-ref:
https://github.com/vercel/next.js/pull/89688#discussion_r2865847895

---------

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-17 14:37:26 +01:00
Josh Story 410c65808a Add static error when unstable_instant is used without cacheComponents (#90793)
`unstable_instant` requires `cacheComponents` to function — without it,
validation is entirely skipped and the config silently does nothing.
This adds a compile-time error in the SWC transform that catches the
misconfiguration early, matching the existing pattern used for segment
configs like `revalidate` that are incompatible with `cacheComponents`
(but inverted — `unstable_instant` requires it rather than being
incompatible with it). The error points directly to the export in the
user's source file.
2026-03-03 00:35:56 +00:00
Jiachi Liu 3aad67b75c feat: server action logging (#88277) 2026-01-16 15:09:02 +01:00
Tim Neutkens ae17e48bbf Update generateMetadata in client component error (#88172)
## What?

Improves the error shown when `generateMetadata` or `metadata` is
exported from a Client Component.

Also updated the documentation to further explain why it can only be
used in a server component and the steps to follow to resolve the error.
2026-01-07 09:45:44 +01:00
Zack Tanner b1ce6042be add compilation error for taint when not enabled (#88173)
Using `taint` APIs from React without enabling `experimental.taint` will
error at runtime because it needs to bundle experimental React, making
it hard to catch and potentially leading to downtime. This updates our
transform to throw an error if we detect that it's imported but `taint`
isn't enabled.

Fixes NAR-690
2026-01-06 11:57:46 -08:00
Hendrik Liebau 3d30f407c4 Allow exporting object and array literals in 'use cache' files again (#86655)
In #86014 we added a validation for exporting object and array literals
from `'use cache'` files. This was a bit too restrictive, and
short-sighted, as it would prevent exporting common things like metadata
or viewport objects from page and layout files, if the file uses a `'use
cache'` top-level directive.
2025-12-01 10:01:11 +01:00
Hendrik Liebau 59fe222b5e Convert any export from a 'use cache' module to a cache function (#86014)
Previously, in a `'use cache'` module, we only compiled statically-known
exported functions and arrow expressions to cache functions. Now, we
also compile re-exports and exports of values that might be functions.

This includes exports like:
- `export { getData } from './data'` - re-exporting from another module
- `export const aliased = getData` - exporting an imported identifier
- `const Page = withSlug(...); export default Page` - exporting the
result of a function call

These cases can't be statically verified as functions at compile time,
so we generate runtime wrappers with `typeof === "function"` checks
(simplified):

```js
let $$RSC_SERVER_CACHE_getData = getData;
if (typeof getData === "function") {
    $$RSC_SERVER_CACHE_getData = React.cache(function() {
        return cache("default", "...", 0, getData, arguments);
    });
    registerServerReference($$RSC_SERVER_CACHE_getData, "...", null);
    Object.defineProperty($$RSC_SERVER_CACHE_getData, "name", { value: "getData" });
}
export { $$RSC_SERVER_CACHE_getData as getData };
```

This ensures that all function exports from `'use cache'` modules are
properly cached, while non-function exports pass through unchanged.

This approach comes with some limitations compared to statically-known
function exports:
1. **No compile-time type checking:** We can't verify that these exports
are actually functions. Non-function exports will be passed through
as-is at runtime.
2. **No async enforcement:** We can't enforce that functions must be
async (as we do for statically-known functions). Synchronous functions
are supported, but will lie about their type signature. They become
async after being wrapped as cache functions, even though their original
signature was synchronous.

closes NAR-196
2025-11-26 15:30:25 +01:00
Hendrik Liebau acac0b0567 Fix compilation of exported server functions (#86296)
With this PR we are restructuring how the Next.js compiler transforms server function modules (`'use server'` or `'use cache'` directive at the top of the file).

The workaround for skipping the registration of exported `'use cache'` functions is removed. All exported server functions are now properly registered during visitor traversal, which fixes incorrect server reference information bytes in server reference IDs in some circumstances (see #86292 for example).

We're also adding support for string literal exports, e.g.:

```js
'use server'

async function foo() {}
export { foo as '📙' }

```
2025-11-24 13:54:17 +01:00
Niklas Mischkulnig 33e1764252 Bump swc to 48 (#86240)
Upgrade swc from 45 to 48

Based on #85540

- In the AST, strings now contain a `Wtf8Atom` (= not utf8), while `Ident`s keep using the utf8 `Atom`.
- swc's comment printing logic also changed slighted, moving some comments around
- ~~Some comments are getting lost along the way and/or attached to the wrong line. This also broke server actions which rely on a `__next_internal_client_entry_do_not_use__` at the very top~~
2025-11-21 12:47:13 +01:00
Hendrik Liebau ee6e35a19e Omit unused encryptActionBoundArgs/decryptActionBoundArgs imports (#86015)
When a module does not include any `'use server'` or `'use cache'` closures that use variables from their outer scope, the imports for `encryptActionBoundArgs` and `decryptActionBoundArgs` are unnecessary.

> [!TIP]
> Review with hidden whitespace changes.
2025-11-12 11:19:09 +01:00
Hendrik Liebau ba20a5e8f2 Fix false-positive build error for cacheLife & cacheTag (#85875)
When functions exported from a module with a top-level `'use cache'`
directive are imported into client modules, the compiler should allow
`cacheLife` and `cacheTag` usage within those functions.

This PR updates the `react_server_components` transform to properly
track module directives (`'use client'`, `'use server'`, `'use cache'`)
and skip client graph validation for modules with `'use cache'`,
identical to how modules with `'use server'` are already handled. Those
modules are transformed anyway for the client graph by the
`server_actions` transform, which is applied after the
`react_server_components` transform, in which their implementation is
replaced by server function references.

closes NAR-498
2025-11-11 17:30:41 +01:00
Hendrik Liebau 5bd7c6fec0 [test] Add missing test fixtures for cacheLife & cacheTag in client (#85872)
Importing `cacheLife` and `cacheTag` in Client Components yields build errors. This adds test fixtures to cover these scenarios.

In addition, we're duplicating the test fixtures that have different error messages between `app/` and `pages/` directories to ensure both cases are covered.
2025-11-11 14:37:43 +01:00
Hendrik Liebau b6a7714057 Hoist inner 'use cache' functions to reduce function allocations (#85904)
This PR fixes a slight performance regression introduced in #85519 where
inline async function expressions were created for each unique set of
arguments passed to a `'use cache'` function.

For a function like this:

```js
export async function getCachedData(id) {
  'use cache';
  return fetch(`/api/data/${id}`);
}
```

we were previously generating (simplified):

```js
export var $$RSC_SERVER_CACHE_0 = cache("default", "0815", 0, async function(id) {
  return fetch(`/api/data/${id}`);
});
```

In #85519, we restructured this to ensure proper stack frames in error
messages (simplified):

```js
export var $$RSC_SERVER_CACHE_0 = React.cache(function getCachedData() {
  return cache("default", "0815", 0, async function(id) {
    return fetch(`/api/data/${id}`);
  }, arguments);
});
```

However, this introduced a small performance issue: the inner async
function expression was passed inline to the cache wrapper. While
`React.cache` memoizes the outer function, each unique set of arguments
would cause a new inner function object to be allocated.

Now we're generating (simplified):

```js
const $$RSC_SERVER_CACHE_0_INNER = async function getCachedData(id) {
  return fetch(`/api/data/${id}`);
};

export var $$RSC_SERVER_CACHE_0 = React.cache(function getCachedData() {
  return cache("default", "0815", 0, $$RSC_SERVER_CACHE_0_INNER, arguments);
});
```

The cache implementation is now hoisted to module scope with an `_INNER`
suffix as a const declaration with a named function expression. The
function name is preserved to ensure proper stack traces in dev. This
eliminates the repeated function allocations while preserving the
improvements from #85519.
2025-11-10 13:09:59 +01:00
Zack Tanner da024c190a [cache components] move experimental.cacheHandlers out of experimental (#85157)
Moves this flag out of experimental so that custom `use cache` handlers
can be configured alongside the cache components release.
2025-10-21 10:41:28 -07:00
Zack Tanner 85c3d20a91 [cache components]: move flag out of experimental (#85035)
This moves `experimental.cacheComponents` to a top level config. As part
of this, I disabled some tests in `build-output-prerender` that assert
on `cacheComponents` appearing in the experimental list. In a separate
PR, I'm going to show that Cache Components is enabled next to the
bundler info.

This also updates some docs pages to remove "experimental" language.
2025-10-18 14:13:44 -07:00
Jiwon Choi 28b64e2f8d [Breaking] Remove deprecated unstable_rootParams (#84373)
This PR removed the deprecated `unstable_rootParams`. This feature was
not documented.
2025-10-01 18:49:49 +02:00
Hendrik Liebau 81bccc93e9 Guide users to experimental.cacheComponents config (#84121)
In error messages and in docs for `'use cache'` and related APIs, we now
guide users to the `experimental.cacheComponents` config instead of
`experimental.useCache`.

closes NAR-410
2025-09-23 12:23:59 +02:00
Hendrik Liebau 99b078b8da Emit build error when 'use cache' directive is misspelled (#83756)
Handles cases like:
- `'use cache:remote'` (missing space after colon)
- `'use cache : default'` (extra space before colon)
- `'use cache private'` (missing colon)
- `'use cache: '` (missing cache kind)

closes NAR-392
2025-09-13 20:59:39 +02:00
Wyatt Johnson 8abbb3dc2e refactor: rename experimental.dynamicIO to experimental.cacheComponents (#81562)
## What?

Rename `experimental.dynamicIO` to `experimental.cacheComponents` across
the Next.js codebase.

## Why?

We're going to be merging the functionality of the `ppr`, `dynamicIO`
and `useCache` experimental flags into the singular `cacheComponents`
flag to reduce complexity of the codebase and simplify adoption for
users wanting to experiment with experimental features.

## How?

- Renamed the configuration option from `experimental.dynamicIO` to
`experimental.cacheComponents`
- Added deprecation handling with automatic migration for the old option
name
- Updated all documentation, tests, and internal references
- Updated Rust code in SWC transforms and Turbopack
- Maintained backward compatibility with deprecation warnings

NAR-158
2025-07-17 19:30:28 -06:00
Hendrik Liebau 08209b9bf4 Improve error message for sync server functions (#81705)
When a server action or `'use cache'` function is defined as a
synchronous function, the error message now only marks the function name
for better clarity. Previously, the entire function body was marked,
which looks fine in small test fixtures, but is pretty messy for larger
functions, both in the terminal as well as in the dev error overlay.

For anonymous default exports we can't optimize this and are still
marking the entire function body, as we don't have a name to refer to.

closes NAR-165
2025-07-16 13:33:20 +02:00
Joseph 74be505943 Update help link for inlined use server in client components (#81495)
Partially Closes:
https://linear.app/vercel/issue/DOC-4854/server-actions-v14-and-v13-links
2025-07-14 07:36:55 +00:00
Hendrik Liebau 4d3a02d86f Allow server functions to be used as client component props in 'use cache' (#81431)
When passing server actions or nested `'use cache'` functions inside of
cached components as props to client components, we need to make sure
that those are registered as server references, even when restoring the
parent component from the cache, e.g. during the resume of a partially
static shell. Otherwise, React would throw a runtime error while trying
to serialize the props.

<details>
<summary>Example</summary>

```tsx
import { connection } from 'next/server'
import { Suspense } from 'react'

export default function Page() {
  return (
    <div>
      <Suspense fallback={<h1>Loading...</h1>}>
        <Dynamic />
      </Suspense>
      <CachedForm />
    </div>
  )
}

const Dynamic = async () => {
  await connection()
  return <h1>Dynamic</h1>
}

async function CachedForm() {
  'use cache'

  return (
    <form
      action={async () => {
        'use server'
        console.log('Hello, World!')
      }}
    >
      <button>Submit</button>
    </form>
  )
}
```
</details>

Previously, for inline server functions, the Next.js compiler placed the
`registerServerReference` calls where the server function was originally
declared. When the enclosing function was restored from a cache, this
call was skipped and the reference was not registered, leading to the
serialization error. To fix it, we can hoist the
`registerServerReference` call into the module scope, where the
reference itself also has been hoisted to. For simplicity, we're doing
this now generally, regardless of whether the server function is inline
or top-level.

Note: Since `registerServerReference` uses `Object.defineProperties` to
mutate the given reference, we don't need to assign the result to
anything. We already did this for exported functions of a module with a
top-level `'use server'` directive.

closes NAR-167
2025-07-09 15:41:15 +02:00
Josh Story 7e44fe350e disallow unstable_rootParams in client components (#79471)
unstable_rootParams is a server component API and should not be allowed
to be accessed from client components. This change adds a compiler error
if this import is detected within the client module graph.
2025-05-21 13:49:58 -07:00
Jiwon Choi fd438787f3 misc: use correct capitals for React terms (#78445)
- `Client/Server Component`
- `React Hooks`

---------

Co-authored-by: Sebastian "Sebbie" Silbermann <sebastian.silbermann@vercel.com>
2025-04-23 21:50:55 +09:00
Donny/강동윤 3a30312049 refactor(actions): Remove turbopack magic comments (#77063)
### What?

Remove magic comments for disabling export merging in the advanced tree shaking of turbopack.

### Why?

Turbopack magic comments for tree shaking + server actions are not required anymore since https://github.com/vercel/next.js/pull/76938.
2025-03-14 15:27:55 +09:00
Janka Uryga 03a600c5b8 fix: workaround for TS bug in Object.defineProperty (#75944)
This PR changes the server action generated code a bit to work around a
typescript bug and remove some false positives we got while
typechecking: https://github.com/microsoft/TypeScript/issues/61165

The trick is that typescript seems to look for *exactly*
`Object.defineProperty(obj, 'literal', { value: ... })`, and changing
any part of the expression bypasses the bug, so we can just use
`Object['defineProperty']` instead.
2025-02-25 15:30:03 +00:00
Jude Gao 8db39d6c00 fix: client-only error message format (#75446)
Looking at the test file change should convince you that this fixes the formatting issue.

https://linear.app/vercel/issue/NDX-671/error-from-client-only-package-is-malformed
2025-01-29 19:33:59 -05:00
Hendrik Liebau 7aa61377fb Emit build error when useCache is enabled and Edge runtime is used (#75279)
The `"use cache"` directive is not compatible with the Edge runtime.

When the `experimental.useCache` flag is enabled, we now emit a build
error for pages with `export const runtime = 'edge'`. This is analogous
to using the `experimental.dynamicIO` flag.

The other route segment configs that are forbidden when `dynamicIO` is
enabled, are currently still allowed for `useCache`:

- `dynamicParams`
- `dynamic`
- `fetchCache`
- `revalidate`
2025-01-24 17:33:24 +00:00
Hendrik Liebau bc3e597ae5 Fix export statement validations for app router pages (#75278)
Fixes all issues related to the current export statement validation for
app router pages, as highlighted in #75277.
2025-01-24 17:55:26 +01:00
Hendrik Liebau 958b9669fb Add test fixtures demonstrating export statement validation bugs (#75277)
The issues with the current export statement validation for app router
pages are documented in the added fixtures, see also the inline comments
in the diff.
2025-01-24 17:24:02 +01:00
Hendrik Liebau bf726d3c21 Introduce standalone experimental.useCache flag (#75240) 2025-01-24 00:28:05 +01:00
Hendrik Liebau 847506f716 Emit build error if 'use action' directive is used (#74954) 2025-01-16 01:07:46 +01:00
Hendrik Liebau 214e0d3252 Forbid super in static class methods with server function directives (#73061) 2024-11-26 19:19:16 +01:00
Hendrik Liebau 12c483aa4d Don't hoist a server function if a compile error was emitted (#73189)
This allows us to skip unnecessary work, and also avoids generating invalid code, e.g. not allowed `super` calls in hoisted methods.
2024-11-26 17:24:10 +01:00
Hendrik Liebau 6602327c72 Handle server function directives in class methods (#73060)
With this PR, we are allowing _static_ class methods to be annotated with `"use cache"` or `"use server"`.

Class _instance_ methods on the other hand are not allowed as server functions.
2024-11-25 15:26:24 +01:00
Hendrik Liebau adfdd09d53 Omit unnecessary cache wrapper import (#73160)
When a `"use cache"` module has no exported or annotated cache function, we can omit the import statement for the cache wrapper function.
2024-11-25 15:00:38 +01:00
Hendrik Liebau c53ee73cf1 Forbid this and arguments in server functions (#73059)
Accessing `this` or `arguments` in server functions is not allowed. With
this PR, we are now emitting build errors in this case.

---------

Co-authored-by: Janka Uryga <lolzatu2@gmail.com>
2024-11-23 23:46:24 +01:00
Hendrik Liebau 04adb76405 Disallow multiple server directives at the same level (file or function) (#73018)
This PR has two main goals:

- **Consolidate the detection of server directives in modules and function bodies.** Previously, these were two separate implementations with significant overlap, but also some [questionable discrepancies](https://github.com/vercel/next.js/pull/72811#discussion_r1843660381).
- **Model the current directive (in a file or function) using an enum instead of two separate booleans.** This change prevents any confusion that both directives might be present simultaneously. Additionally, we're now emitting an error if multiple directives (`"use server"` and `"use cache"`) are defined in the same location (at the top of a file or function body).

A mixed usage of `"use server"` and `"use cache"` _across different locations_ is still allowed, e.g. `"use cache"` at the top of a file, and `"use server"` in a function.

> [!NOTE]  
> The diff may be tricky to review because chunks from two different functions are combined into a single function. Fortunately, we have comprehensive test coverage for the transforms, which instills high confidence that these changes are correct.
2024-11-22 21:35:44 +01:00
Donny/강동윤 c8f96d50ff perf(turbopack): Enable merging of export fragments (#72813)
### What?

Enable single-incoming-edge optimization for module fragments with `ModulePart::Export`. 

### Why?

It will reduce the number of modules greatly.



### How?

Closes PACK-3481
2024-11-21 23:06:41 +09:00
Hendrik Liebau 44cb7dddfe Consolidate FnExpr and FnDecl server function transforms (#72960)
By adding `visit_mut_function` we can deduplicate the code that's common between `visit_mut_fn_expr` and `visit_mut_fn_decl`. And more importantly, this prepares us for handling the transform of method props, which will also use `visit_mut_function`.

As a positive side effect this brings two small improvements with it:
- always highlight the whole function when `async` is missing, not only the identifier
- assign a function name to an anonymous arrow function expression in a variable declaration
2024-11-20 09:07:01 +01:00
Hendrik Liebau d54122a712 Emit build error for unknown cache kinds (#72832)
When a `"use cache"` directive with a custom cache kind is used, e.g.
`"use cache: custom"`, a cache handler with the same name must be
specified in the Next.js config:

```js
/**
 * @type {import('next').NextConfig}
 */
const nextConfig = {
  experimental: {
    dynamicIO: true,
    cacheHandlers: {
      custom: require.resolve('path/to/custom/cache/handler'),
    },
  },
}

module.exports = nextConfig
```

If this is not the case, we emit a build error with an error message
that explains this requirement.

<img width="795" alt="Screenshot 2024-11-14 at 23 30 01"
src="https://github.com/user-attachments/assets/bd138c97-608f-42ce-abb2-edb3e44edc3f">

When we'll get a docs page for this experimental config, we will add the
usual "Read more: ..." hint as well.

---------

Co-authored-by: Benjamin Woodruff <benjamin.woodruff@vercel.com>
Co-authored-by: Janka Uryga <lolzatu2@gmail.com>
2024-11-17 17:03:16 +01:00
Hendrik Liebau 0f9f442b7b Consolidate server actions transform errors into emit_error function (#72811)
This improves maintainability and readability by decluttering the server actions transforms implementation, dedupes a few error messages, and consistently uses periods and trailing newlines for all error messages.

The same approach is already used for the React Server Components transforms.
2024-11-15 13:55:19 +01:00
Hendrik Liebau 2c6b09d57d Emit build error when "use cache" is used without dynamicIO enabled (#72781)
Using a build error instead of a runtime error allows us to fail early,
and show a proper error source in the terminal and in the dev overlay.

<img width="777" alt="Screenshot 2024-11-13 at 22 19 45"
src="https://github.com/user-attachments/assets/d0ee3c69-71f5-4aa6-8c0a-879217f66930">
2024-11-13 21:43:58 +00:00