Common Errors
A catalog of error messages you may encounter when developing with EreoJS, along with their causes and solutions.
Cannot find module '@ereo/...'
Error message:
error: Cannot find module '@ereo/client'Cause: Dependencies are not installed or are out of sync. This typically happens after cloning a repo, switching branches, or updating package.json.
Fix:
bun installIf that does not resolve it, remove the lockfile and node_modules, then reinstall:
rm -rf node_modules bun.lock
bun installIf you are in a monorepo, make sure the package is listed in your app's package.json with workspace:*:
{
"dependencies": {
"@ereo/client": "workspace:*",
"@ereo/data": "workspace:*"
}
}Port already in use
Error message:
error: Failed to start server — port 3000 is already in useCause: Another process is already listening on the default port (3000). This is often a previous dev server that was not shut down cleanly.
Fix:
Option 1 --- Change the port in ereo.config.ts:
import { defineConfig } from '@ereo/core'
export default defineConfig({
server: { port: 3001 },
})Option 2 --- Use the PORT environment variable:
PORT=3001 bun devOption 3 --- Kill the process occupying the port:
# macOS / Linux
lsof -ti:3000 | xargs kill -9
# Windows
netstat -ano | findstr :3000
taskkill /PID <PID> /Floader is not a function
Error message:
TypeError: loader is not a functionCause: The route file does not export a loader as a named export, or the export is not a function. This can happen if you use export default instead of a named export, or if the file exports an object instead of a function.
Fix:
Ensure your route file uses a named export:
// Correct
export const loader = createLoader(async ({ params }) => {
return { data: 'value' }
})
// Also correct — plain function export
export async function loader({ params }) {
return { data: 'value' }
}
// Wrong — default export
export default async function ({ params }) {
return { data: 'value' }
}Hydration mismatch
Error message:
Warning: Text content did not match. Server: "..." Client: "..."Cause: The HTML rendered on the server differs from what React expects on the client. Common causes include:
- Using
Date.now(),Math.random(), or other non-deterministic values during render - Accessing
windowordocumentin a component that runs on both server and client - Rendering different content based on client-only state (like
localStorage) - A non-island component that relies on browser APIs
Fix:
For components that need browser APIs, mark them as islands so they only hydrate on the client:
// components/CurrentTime.island.tsx
'use client'
import { useState, useEffect } from 'react'
export default function CurrentTime() {
const [time, setTime] = useState('')
useEffect(() => {
setTime(new Date().toLocaleTimeString())
}, [])
return <span>{time}</span>
}For non-deterministic data, move it into a loader so the same value is used on both server and client:
export const loader = createLoader(async () => {
return { timestamp: Date.now() }
})Failed to parse route
Error message:
error: Failed to parse route file: routes/my route.tsxCause: The route file name contains invalid characters (spaces, special characters) or does not follow EreoJS file naming conventions.
Fix:
Follow the route file naming conventions:
| Pattern | Example | Purpose |
|---|---|---|
index.tsx | routes/index.tsx | Index route |
[param].tsx | routes/posts/[id].tsx | Dynamic segment |
[[param]].tsx | routes/posts/[[page]].tsx | Optional segment |
[...slug].tsx | routes/docs/[...slug].tsx | Catch-all |
_layout.tsx | routes/_layout.tsx | Layout wrapper |
_error.tsx | routes/_error.tsx | Error boundary |
(group)/ | routes/(marketing)/ | Route group |
File names must use lowercase letters, hyphens, and the special bracket syntax. Avoid spaces and uppercase letters. See the Route Conventions reference for the complete list.
CSRF token mismatch
Error message:
403 Forbidden: CSRF token mismatchCause: The CSRF protection middleware rejected the request because the token is missing or does not match. This happens when:
- The form does not include the CSRF token field
- The token has expired (session timeout)
- A third-party client is submitting forms without the proper token
Fix:
When using <Form> from @ereo/client, the CSRF token is included automatically. For standard HTML forms, include the token manually:
import { useCsrfToken } from '@ereo/client'
export default function MyForm() {
const csrfToken = useCsrfToken()
return (
<form method="post">
<input type="hidden" name="_csrf" value={csrfToken} />
<input name="title" />
<button type="submit">Submit</button>
</form>
)
}If CSRF is causing issues during development, verify the middleware is configured:
// ereo.config.ts
import { defineConfig } from '@ereo/core'
export default defineConfig({
server: {
csrf: {
enabled: true,
cookieName: '_csrf',
},
},
})See the Security guide for more on CSRF protection.
Module has no default export
Error message:
error: Route "routes/about.tsx" has no default exportCause: Page routes must export a default React component. API-only routes (those exporting only GET, POST, etc.) do not need a default export, but page routes do.
Fix:
Add a default export to your route file:
export default function About() {
return <h1>About Us</h1>
}Build failed: external dependency not found
Error message:
error: Could not resolve "some-package"Cause: A package used in your code is not installed, or it needs to be marked as external in the build config.
Fix:
Install the missing package:
bun add some-packageIf the package should not be bundled (e.g., a Node.js built-in or server-only dependency), mark it as external:
// ereo.config.ts
export default defineConfig({
build: {
external: ['some-package'],
},
})Still stuck?
If your error is not listed here, try these steps:
- Search the GitHub Issues
- Check the Debugging guide for tools to investigate further
- Ask in the Discord community