> ## Documentation Index
> Fetch the complete documentation index at: https://docs.edgespark.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Error handling in EdgeSpark

> Patterns for validation errors, database errors, auth failures, and consistent JSON responses in modern EdgeSpark server routes.

Consistent error handling makes your API predictable for clients and easier to debug. This guide shows patterns that fit the current EdgeSpark scaffold and runtime SDK.

## Return a standard error shape

```typescript server/src/index.ts theme={null}
import { db } from "edgespark";
import { auth } from "edgespark/http";
import { eq } from "drizzle-orm";
import { Hono } from "hono";
import { posts } from "@defs";

function err(message: string, details?: unknown) {
  return { error: message, ...(details ? { details } : {}) };
}

const app = new Hono().post("/api/posts", async (c) => {
  let body: { title?: string; content?: string };

  try {
    body = await c.req.json();
  } catch {
    return c.json(err("Request body must be valid JSON"), 400);
  }

  if (!body.title || body.title.trim().length === 0) {
    return c.json(err("Validation failed", { field: "title", issue: "required" }), 422);
  }

  const [post] = await db
    .insert(posts)
    .values({ title: body.title.trim(), content: body.content ?? null, authorId: auth.user.id })
    .returning();

  return c.json(post, 201);
});

export default app;
```

## Handle not found

```typescript server/src/index.ts theme={null}
import { db } from "edgespark";
import { eq } from "drizzle-orm";
import { Hono } from "hono";
import { posts } from "@defs";

const app = new Hono().get("/api/posts/:id", async (c) => {
  const id = Number(c.req.param("id"));

  if (Number.isNaN(id) || id < 1) {
    return c.json({ error: "Invalid ID" }, 400);
  }

  const [post] = await db.select().from(posts).where(eq(posts.id, id));

  if (!post) {
    return c.json({ error: "Post not found" }, 404);
  }

  return c.json(post);
});

export default app;
```

## Catch unexpected errors

```typescript server/src/index.ts theme={null}
import { db } from "edgespark";
import { eq } from "drizzle-orm";
import { Hono } from "hono";
import { posts } from "@defs";

const app = new Hono().get("/api/posts/:id", async (c) => {
  try {
    const id = Number(c.req.param("id"));
    const [post] = await db.select().from(posts).where(eq(posts.id, id));

    if (!post) return c.json({ error: "Not found" }, 404);
    return c.json(post);
  } catch (error) {
    console.error("Failed to fetch post", { id: c.req.param("id"), error });
    return c.json({ error: "Internal server error" }, 500);
  }
});

export default app;
```

## Register global handlers

```typescript server/src/index.ts theme={null}
import { Hono } from "hono";

const app = new Hono();

app.onError((error, c) => {
  console.error("Unhandled error", { path: c.req.path, error });
  return c.json({ error: "Internal server error" }, 500);
});

app.notFound((c) => {
  return c.json({ error: "Not found" }, 404);
});

export default app;
```

## Standard status codes

| Situation                     | Status |
| ----------------------------- | ------ |
| Request body invalid JSON     | 400    |
| Missing or invalid parameters | 422    |
| Not authenticated             | 401    |
| Authenticated but not allowed | 403    |
| Resource not found            | 404    |
| Success, returns body         | 200    |
| Created successfully          | 201    |
| Success, no body              | 204    |
| Server error                  | 500    |

<Note>
  The platform automatically returns `401` for unauthenticated requests to `/api/*` routes before your handler runs. You usually only need to handle `403 Forbidden` yourself.
</Note>

## See also

<Columns cols={2}>
  <Card title="Build a REST API" icon="rocket" href="/tutorials/build-a-rest-api">
    A complete example with validation, auth, uploads, and error handling.
  </Card>

  <Card title="View logs" icon="terminal" href="/guides/logs">
    Read runtime logs and exceptions from your deployed project.
  </Card>
</Columns>
