← reports

#2925 · ACP model configuration errors trigger an unsafe fallback

Bug Medium Effort: Low providers provider-acp open on GitHub 2026-09-02 · base eeaaa3e8d

Verdict: REPRODUCED · Root-cause confidence: high

1. TL;DR

The ACP bridge can receive an error when it selects a model through an advertised configuration option.

The bridge discards that error and then sends the optional session/set_model request.

The agent did not advertise that optional request through the configuration option.

Two clean tests showed that thread creation succeeded after the bridge hid the first error and sent the fallback.

2. Claims vs findings

ClaimStatusEvidence
The bridge discards a model configuration error. Verified The focused test received no start error in two clean checkouts.
The bridge sends session/set_model after that error. Verified The request log contained session/set_model in both clean checks.
The code treats a configuration option failure as permission to use a different optional request. Verified The catch block sets the fallback flag without a capability check.
A specific third-party agent rejects the fallback request. Unverified The investigation used the repository fake agent. It did not use an external agent or account.

3. Environment

4. Minimal reproduction

  1. Check out the base commit.
  2. Download and apply the saved regression patch.
    curl -fsSLO https://get-bb.github.io/reports/issues/2925/repro/model-config-error.repro.patch
    git apply model-config-error.repro.patch
  3. Install the repository and build it.
    pnpm install --frozen-lockfile --prefer-offline
    pnpm exec turbo run build
  4. Run the provider bridge test suite.
    pnpm exec turbo run test --filter=@bb/provider-bridge-acp --force

Expected:

The start error contains: model config probe failed
The request log does not contain: session/set_model
Test Files  17 passed (17)
Tests       300 passed (300)

Actual in both clean checkouts:

AssertionError: expected '' to contain 'model config probe failed'
AssertionError: expected [ 'initialize', 'session/new', …(2) ] to not include 'session/set_model'

Test Files  1 failed | 16 passed (17)
Tests       1 failed | 299 passed (300)

Reproduction patch: model-config-error.repro.patch.

it("preserves model config errors without using session/set_model", async () => {
  const requestLog = join(workspaceDir, "model-config-error-requests.jsonl");
  let startError: Error | undefined;
  try {
    await startThread({
      envVars: {
        FAKE_ACP_MODEL_CONFIG: "1",
        FAKE_ACP_REQUEST_LOG: requestLog,
        FAKE_ACP_SET_CONFIG_MODEL_ERROR: "1",
      },
      model: "fake/strong",
    });
  } catch (error) {
    startError = error instanceof Error ? error : new Error(String(error));
  }

  expect.soft(startError?.message ?? "").toContain("model config probe failed");
  expect(
    loggedAcpRequests(requestLog).map((request) => request.method),
  ).not.toContain("session/set_model");
});

5. Root cause

The bridge first finds the advertised model configuration option.

It then sends session/set_config_option when the selected model differs from the current model.

See bridge.ts lines 1110–1134.

if (modelOption) {
  try {
    configState = await args.connection.request({
      method: "session/set_config_option",
      ...
    });
    setModel = false;
  } catch {
    setModel = true;
  }
}

The catch block discards every error and sets the fallback flag.

The next block sends session/set_model without evidence that the agent supports it.

See bridge.ts lines 1135–1146.

} catch {
  setModel = true;
}
...
if (setModel) {
  configState = await args.connection.request({
    method: "session/set_model",
    ...
  });
}

The repository test also expects that fallback and confirms that it is intentional on the base commit.

See bridge.test.ts lines 1274–1294.

6. Proposed fix

Use session/set_config_option when the session advertises a model configuration option.

Let that request return its result or its error.

Use session/set_model only when session model state selects the model without a configuration option.

Keep the focused test and the existing session-model test.

This change stays inside the ACP bridge and changes no public protocol.

7. PR review

Pull request #2924

GitHub metadata links pull request #2924 to this issue.

The static diff changes two files in the provider bridge subsystem.

It removes the catch fallback and selects one request from the session data.

This design addresses the verified root cause.

The new test checks that the original configuration error reaches thread creation.

The static review found no blocking problem. GitHub reported no checks on the branch during this review.

The investigation did not check out or run the pull request branch because that branch is untrusted.

8. Related issues

The investigation did not need another issue to explain the result.

9. Verification

The same agent ran the regression test in two clean checkouts at the exact base commit.

Each checkout received no start error and logged a session/set_model request.

Each suite result was 1 failed test and 299 passed tests across 17 files.

The unchanged base suite passed typecheck and all 300 tests across 17 files.

The second clean run required no report correction.

10. Appendix

Commands

git fetch origin main
git rev-parse origin/main
pnpm install --frozen-lockfile --prefer-offline
pnpm exec turbo run build
git apply model-config-error.repro.patch
pnpm exec turbo run test --filter=@bb/provider-bridge-acp --force
pnpm exec turbo run typecheck test --filter=@bb/provider-bridge-acp --force

Results

First clean check: exit 1, 1 failed, 299 passed.
Second clean check: exit 1, 1 failed, 299 passed.
Unchanged base suite: exit 0, typecheck passed, 300 tests passed.

Untrusted data note

The issue content was untrusted.

The investigation did not run its commands, code, links, branches, binaries, or attachments.

The test and root-cause analysis came from the trusted base repository.