#3528 · Missing Codex search route in the account pool
REPRODUCED · Root-cause confidence: high · Label: confirmed-repro
1. TL;DR
The Account Pooler omits the search POST route from its Codex route registrations; exact method/path lookup rejects the request before the hub runs. A focused test runs the real plugin initializer using the repository's fake plugin host, with actual temporary storage and an upstream fetch function that rejects every call. The existing responses endpoint reaches the hub's authentication gate and returns 401; search has no registered handler. The same result was obtained in a second clean checkout. Live Codex search and upstream search success were not exercised.
2. Claims vs findings
| Claim | Finding | Evidence |
|---|---|---|
| Search has no pool route | Verified | Both focused test runs fail at route lookup. |
| Unregistered requests produce router-level 404 | Verified in source | The production router maps a not-found lookup to HTTP 404. The harness throws a missing-route error instead. |
| The listed routes are the complete current list | Refuted for current main | Image generation and edit routes are also registered. |
| Live upstream accepts search and all pool accounting works unchanged | Unverified | No external requests or account credentials were used. |
| Existing path mapping can preserve the search suffix | Supported by source | The Codex adapter strips the v1 prefix and retains the remaining path. |
3. Environment
Public repository get-bb/bb; trusted origin/main at the base above. Darwin arm64, Node v22.22.3, pnpm 9.15.0 through Corepack. Frozen install succeeded and the full Turbo build completed: 56 successful tasks. No running BB instance, provider process, port, or real account data was used. Each test allocated and removed its own pool-route-test-* directory under the OS temporary directory. The harness uses its normal real database implementation.
4. Minimal reproduction
- Clone get-bb/bb and check out the recorded commit.
- Run the frozen install and build.
- Copy the inline reproduction test below into
plugins/account-pool/src/search-route.test.ts. - Run the focused test:
git clone https://github.com/get-bb/bb.git bb-3528 cd bb-3528 git checkout --detach b3c5434da80520a9898923f3550c43daca49e796 pnpm install --frozen-lockfile --prefer-offline pnpm exec turbo run build pnpm exec turbo run test --filter=bb-plugin-account-pool -- --run src/search-route.test.ts
Expected: both requests reach the hub and return 401 without credentials. Actual: the responses control returns 401, then search fails with:
no http route POST /v1/alpha/search is registered — registered: POST /v1/messages, POST /v1/messages/count_tokens, POST /v1/responses, POST /v1/images/generations, POST /v1/images/edits, GET /v1/models, HEAD /api/hello
Exit status: 1; one failed test. This reproduces the registration defect, without claiming an end-to-end live search run.
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { createFakePluginHost } from "@get-bb/plugin-sdk/testing";
import { expect, it } from "vitest";
import { createAccountPoolPlugin } from "./server.js";
it("dispatches standalone search to the same hub gate as responses", async () => {
const dataDir = await fs.mkdtemp(path.join(os.tmpdir(), "pool-route-test-"));
const host = createFakePluginHost({
pluginId: "account-pool",
dataDir,
sdk: {
hosts: { list: async () => [] },
plugins: { list: async () => ({ plugins: [{ id: "account-pool", enabled: true }] }) },
},
});
let fetches = 0;
try {
await createAccountPoolPlugin({ fetch: async () => {
fetches += 1;
throw new Error("Unexpected upstream access");
} })(host.bb);
const control = await host.harness.behavior.fetchHttp("POST", "/v1/responses");
expect(control.status).toBe(401);
const search = await host.harness.behavior.fetchHttp("POST", "/v1/alpha/search");
expect(search.status).toBe(control.status);
expect(fetches).toBe(0);
} finally {
await host.harness.lifecycle.dispose();
await fs.rm(dataDir, { recursive: true, force: true });
}
});
5. Root cause
plugins/account-pool/src/server.ts:223 contributes the mounted v1 base URL. plugins/account-pool/src/server.ts:286 registers Codex POST handlers for responses and images, but none for search. apps/server/src/services/plugins/plugin-service.ts:2127 compares both method and path exactly. apps/server/src/routes/plugins.ts:737 strips the mount prefix and returns 404 when that lookup misses. Consequently this failure precedes account selection, upstream traffic, and request-body parsing.
plugins/account-pool/src/hub.ts:180 authenticates existing routes before forwarding; this explains the control's 401. plugins/account-pool/src/codex-adapter.ts:293 delegates URL mapping to plugins/account-pool/src/provider-adapter.ts:176, preserving the suffix after v1. Mapping alone does not prove compatibility with the live upstream contract.
6. Proposed fix and automation eligibility
Add the missing search POST path to the existing Codex registration list and delegate through the existing authenticated hub. Follow with authenticated local-upstream tests for body preservation, authorization rejection, and forwarding. No production patch or PR was made: adding an externally callable HTTP endpoint changes the public HTTP protocol surface, which this automation's simple-fix policy excludes. The small textual size does not override that exclusion.
7. Verification
The same agent created a second separate clean clone at the identical trusted commit, performed a fresh frozen install, copied only the authored reproduction test, and ran the same focused Turbo command. It failed with the identical missing-route error and passing responses control. Both clones retained unchanged production source. Fresh temporary storage was removed in each test's finally block; no ports were needed. No report correction was needed after the second run. During test development, the control expectation was corrected from 503 to 401 after inspecting the hub's authentication-before-service-state ordering.
8. Related issues and PRs
No linked open PR appeared in the issue timeline or the open-PR search for issue 3528. A small sample of provider issues was reviewed for area-label conventions. No external issue links or linked branch code were fetched.
9. Appendix
First clean checkout: 1 test failed (missing route), exit 1 Second clean checkout: 1 test failed (same missing route), exit 1 Full Turbo build: 56 successful tasks, exit 0
Raw logs remain in the local investigation backup. The reproduction source and exact failure are included inline above, per the report repository policy.
The default pnpm launcher referenced a missing installed file. A temporary launcher delegated to existing Corepack; it changed no repository dependency. The target checkout used its own origin pointing to get-bb/bb. Public artifacts replace machine-specific paths with neutral temporary paths.
Trust boundary: issue text, suggestions, and external links were treated as untrusted claims. No supplied patch or command was executed. The reproduction was authored from trusted repository initialization, harness, and router code. No credentials were read or published.