[GH-ISSUE #2588] fix(verify): query-bearing chat completion requests return 404 #905

Closed
opened 2026-10-15 18:29:27 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 13, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/2588

Summary

The loopback OpenAI fixture rejects a chat completion request when its URL includes a query string. Identical request bodies succeed at the same endpoint without the query, so a caller that includes a query cannot reach the fixture's completion callback.

Steps to reproduce

With the locked dependencies installed, run the following from the repository root. It uses disposable output and closes the real fixture after sending the requests.

node --input-type=module <<'JS'
import { mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { createOpenAiModelFixture } from './scripts/verify/clients/model-fixture.mjs';
const directory = await mkdtemp(join(tmpdir(), 'fixture-query-'));
let calls = 0;
const fixture = await createOpenAiModelFixture({directory, prefix: 'probe', onRequest: () => {
  calls++;
  return {role: 'assistant', content: 'ok'};
}});
try {
  const statuses = [];
  for (const path of ['/chat/completions', '/chat/completions?stream=true', '/completions']) {
    const response = await fetch(fixture.baseUrl + path, {
      method: 'POST', headers: {'content-type': 'application/json'},
      body: JSON.stringify({model: 'rea-client-fixture', messages: [{role: 'user', content: 'hi'}]})
    });
    statuses.push(response.status);
  }
  console.log(JSON.stringify({statuses, requests: fixture.requests.length, calls}));
} finally {
  await fixture.close();
  await rm(directory, {recursive: true});
}
JS

Expected behavior

Both requests to the chat completions pathname should reach the completion callback and return 200. The query component does not select a different endpoint. The unrelated completions pathname should return 404.

Actual behavior

The real fixture returned statuses [200,404,404], admitted one request and invoked its callback once. It classified the query-bearing chat completion request as an unsupported endpoint. No real client was run, and no current client's use of query parameters was established.

Evidence

  • Expected source: AGENTS.md:29 requires format-aware boundary parsing; scripts/verify/clients/model-fixture.mjs:64 advertises chat completions, and scripts/verify/clients/anthropic-model-fixture.mjs:26 routes the sibling fixture using the parsed pathname.
  • Failure source: scripts/verify/clients/model-fixture.mjs:50 compares the raw request target including its query component; the loopback probe above returned [200,404,404].
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

Restoration check

Run the same requests and observe [200,200,404], two admitted requests and two callback invocations. Preserve rejection of the unrelated endpoint. Streaming behavior should still follow the body rather than this routing-only query parameter.

Runtime or environment

Source revision 8b181716ac with locked dependencies; Node.js v26.10.0 and npm 11.19.1.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A valid query-bearing request cannot exercise the completion callback of the client-verification fixture. This is a bounded verification defect; current real-client incidence was not observed.
  • Workaround status: available
  • Workaround basis: The same body at the query-free endpoint returned 200. Callers that control the URL can omit the query for this fixture run.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2588 ## Summary The loopback OpenAI fixture rejects a chat completion request when its URL includes a query string. Identical request bodies succeed at the same endpoint without the query, so a caller that includes a query cannot reach the fixture's completion callback. ## Steps to reproduce With the locked dependencies installed, run the following from the repository root. It uses disposable output and closes the real fixture after sending the requests. ```sh node --input-type=module <<'JS' import { mkdtemp, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { createOpenAiModelFixture } from './scripts/verify/clients/model-fixture.mjs'; const directory = await mkdtemp(join(tmpdir(), 'fixture-query-')); let calls = 0; const fixture = await createOpenAiModelFixture({directory, prefix: 'probe', onRequest: () => { calls++; return {role: 'assistant', content: 'ok'}; }}); try { const statuses = []; for (const path of ['/chat/completions', '/chat/completions?stream=true', '/completions']) { const response = await fetch(fixture.baseUrl + path, { method: 'POST', headers: {'content-type': 'application/json'}, body: JSON.stringify({model: 'rea-client-fixture', messages: [{role: 'user', content: 'hi'}]}) }); statuses.push(response.status); } console.log(JSON.stringify({statuses, requests: fixture.requests.length, calls})); } finally { await fixture.close(); await rm(directory, {recursive: true}); } JS ``` ## Expected behavior Both requests to the chat completions pathname should reach the completion callback and return 200. The query component does not select a different endpoint. The unrelated completions pathname should return 404. ## Actual behavior The real fixture returned statuses [200,404,404], admitted one request and invoked its callback once. It classified the query-bearing chat completion request as an unsupported endpoint. No real client was run, and no current client's use of query parameters was established. ## Evidence - Expected source: AGENTS.md:29 requires format-aware boundary parsing; scripts/verify/clients/model-fixture.mjs:64 advertises chat completions, and scripts/verify/clients/anthropic-model-fixture.mjs:26 routes the sibling fixture using the parsed pathname. - Failure source: scripts/verify/clients/model-fixture.mjs:50 compares the raw request target including its query component; the loopback probe above returned [200,404,404]. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete ## Restoration check Run the same requests and observe [200,200,404], two admitted requests and two callback invocations. Preserve rejection of the unrelated endpoint. Streaming behavior should still follow the body rather than this routing-only query parameter. ## Runtime or environment Source revision 8b181716aca2cb467be499cba226abfb5ae19141 with locked dependencies; Node.js v26.10.0 and npm 11.19.1. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: A valid query-bearing request cannot exercise the completion callback of the client-verification fixture. This is a bounded verification defect; current real-client incidence was not observed. - Workaround status: available - Workaround basis: The same body at the query-free endpoint returned 200. Callers that control the URL can omit the query for this fixture run.
bsakaguchi 2026-10-15 18:29:27 +00:00
Author
Owner

@morluto commented on GitHub (Oct 14, 2026):

Verified the reported defect is addressed on main by 4d4631ac94.

Root cause and resolution: The loopback OpenAI fixture routes on parsed URL pathname, so query parameters no longer alter endpoint selection.

Validation: tests/evaluation/model-evals/openAiModelFixture.test.ts — routes OpenAI fixture requests by pathname, not query checks query and plain chat paths return 200, unrelated completion route returns 404, callback count is two, and captured request preserves query.

Evidence: CI run 500e: owner unchanged and assertion passed.

Verification scope: Loopback fixture behavior only; no external model-provider compatibility claim.

Current main CI has separate failures under repair; this closure does not claim the complete main workflow is green.

<!-- gh-comment-id:6162513122 --> @morluto commented on GitHub (Oct 14, 2026): Verified the reported defect is addressed on main by 4d4631ac94431bb8aae2fe3edcf1e8227048848d. Root cause and resolution: The loopback OpenAI fixture routes on parsed URL pathname, so query parameters no longer alter endpoint selection. Validation: `tests/evaluation/model-evals/openAiModelFixture.test.ts` — `routes OpenAI fixture requests by pathname, not query` checks query and plain chat paths return 200, unrelated completion route returns 404, callback count is two, and captured request preserves query. Evidence: CI run 500e: owner unchanged and assertion passed. Verification scope: Loopback fixture behavior only; no external model-provider compatibility claim. Current main CI has separate failures under repair; this closure does not claim the complete main workflow is green.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#905
No description provided.