google-gemini / google-gemini/gemini-cli

bug(a2a-server): express.json() mounted after A2A SDK routes breaks JSON-RPC body parsing

Open Beginner friendly
#29,073 3 comments 0 reactions 0 assignees View on GitHub
area/non-interactive effort/medium kind/bug priority/p2 status/bot-triaged
Dominant language
TypeScript
Stars
107k
Forks
14.6k
Avg merge
2d 3h
Merged PRs (30d)
45

Description

### What happened?

In `packages/a2a-server/src/http/app.ts` (main @ 812f7a2bc), `express.json()` middleware is mounted **after** the A2A SDK routes, causing JSON body parsing to fail for SDK-handled endpoints while succeeding for custom routes — creating an asymmetric unauthenticated backdoor.

`app.ts` lines 275-284:
```ts
let expressApp = express();
expressApp.use((req, res, next) => {
requestStorage.run({ req }, next);
});

const appBuilder = new A2AExpressApp(requestHandler, customUserBuilder);
expressApp = appBuilder.setupRoutes(expressApp, ''); // SDK routes: POST /, /.well-known/agent-card.json
expressApp.use(express.json()); // ← TOO LATE for SDK routes

expressApp.post('/tasks', async (req, res) => {
const taskId = uuidv4();
const agentSettings = req.body.agentSettings as AgentSettings|undefined; // works (after middleware)
// ...
});
```

`A2AExpressApp.setupRoutes` registers `POST /` for JSON-RPC (`message/send`, `tasks/get`, etc.) which expects `req.body` to be parsed JSON. With `express.json()` after `setupRoutes`, those SDK routes receive `req.body = undefined`. The handler may throw or silently fail, while custom routes (`/tasks`, `/executeCommand`, `/listCommands`) defined **after** the middleware work correctly.

This compounds the auth issue (#29001): even if auth were enforced via `customUserBuilder`, the SDK's JSON-RPC path is broken, so legitimate authenticated task messages fail while the unauthenticated custom endpoints succeed.

### What did you expect to happen?

`express.json()` should be mounted **before** `appBuilder.setupRoutes`:

```ts
let expressApp = express();
expressApp.use((req, res, next) => { requestStorage.run({ req }, next); });
expressApp.use(express.json()); // ← before SDK routes
const appBuilder = new A2AExpressApp(requestHandler, customUserBuilder);
expressApp = appBuilder.setupRoutes(expressApp, '');
```

Alternatively, the SDK should be verified to add its own JSON parser internally (it does not — checked `@a2a-js/sdk@1.0.1` express handler, which expects `req.body` to already be parsed).

### Client information

- Source-level finding verified against upstream `main` at commit `812f7a2bc`
- File: `packages/a2a-server/src/http/app.ts:275-284`
- Affects all platforms running `@google/gemini-cli-a2a-server`
- SDK version: `@a2a-js/sdk@1.0.1`

### Login information

Not applicable.

### Anything else we need to know?

Sources:
- https://github.com/google-gemini/gemini-cli/blob/812f7a2bc/packages/a2a-server/src/http/app.ts#L275-L284
- Verified `setupRoutes` does not add JSON parsing internally (inspected `node_modules/@a2a-js/sdk/dist/server/express/index.js` — `jsonRpcHandler` reads `req.body` directly)

**Repro:**
1. `await createApp()` (test helper, port 41242)
2. `curl -X POST http://localhost:41242/ -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"message/send","params":{"message":{"role":"user","parts":[{"text":"hi"}]}}}'` → handler sees `req.body = undefined` → 400 or silent failure
3. `curl -X POST http://localhost:41242/tasks -H "Content-Type: application/json" -d '{"agentSettings":{}}'` → `201` (works because after middleware)

Searched existing issues for "express.json", "a2a middleware", "request body undefined" — no open duplicate found.

Contributor guide

Open the contributing guide

Research direction

Start in packages/a2a-server/src/http/app.ts around lines 275-284 and inspect the ordering of express.json() and A2AExpressApp.setupRoutes. Reproduce with the createApp helper and the provided JSON-RPC curl request, then verify that SDK JSON-RPC endpoints parse requests while the existing custom routes still return their expected responses.

Written by the indexing model from the issue text.

Assessment

Tech stack
express, typescript
Domain
api, backend, security
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
88/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.