Skip to content

Web Fetch: Request.headers is a numeric handle, not a Headers instance — breaks every Hono adapter #1649

Description

@proggeramlug

Scope

Under Perry-native compile, new Request(url, { headers }).headers returns a number (the internal FFI handle) instead of a Headers instance. Any code that calls req.headers.get(...), forEach, iteration, etc. immediately throws TypeError: (number).get is not a function. This is the root cause of Hono's app.fetch crashing on the first request — Hono's Context constructor reads request headers via the standard API.

Why

Hono is runtime-agnostic by design: the same (request: Request) => Promise<Response> contract runs on Workers, Bun, Deno, Node, Lambda — and (we want) Perry. The whole pattern depends on Request.headers being a real Headers object so middleware / context / routing can read headers uniformly. Without this, @hono/node-server, @hono/perry-server, any custom Fastify→Hono bridge, and Hono itself all break at the first header access.

This is the precondition for Hono on Perry working at all — every other piece in the stack reaches for req.headers.get(...) somewhere.

Repro (Perry 0.5.1026, perry-src commit f46c4c3)

```typescript
// repro.ts
const req = new Request('http://localhost/x', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: '{"k":1}',
});
console.log('method:', req.method, 'url:', req.url);
console.log('typeof headers:', typeof req.headers);
console.log('headers value:', String(req.headers));
console.log('content-type:', req.headers.get('content-type'));
```

```bash
perry compile repro.ts -o repro && ./repro
```

Observed:

```
method: POST url: http://localhost/x
typeof headers: number
headers value: 0
TypeError: (number).get is not a function
```

Expected (Node / Bun / Deno / Workers):

```
method: POST url: http://localhost/x
typeof headers: object
headers value: [object Headers]
content-type: application/json
```

Acceptance

  • typeof (new Request(url, { headers })).headers === 'object'
  • .headers.get(name), .headers.has(name), .headers.set(name, value), .headers.append(name, value), .headers.delete(name), .headers.forEach(cb), and the entries/keys/values iterators all work per Web Fetch spec
  • A 10-line repro that constructs a Hono app with one route, calls app.fetch(new Request(url, { headers: {...} })), awaits the result and reads it via .text() returns the expected body — no throws, no hangs.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions