[GH-ISSUE #6035] [Bug] Native function matching marks its fixed representation limit as retryable #3684

Closed
opened 2026-10-31 00:43:01 +00:00 by bsakaguchi · 1 comment
Owner

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

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Native analysis or decompilation

Observed behavior

The registered match_native_functions MCP tool refuses observations above its fixed 96 MiB representation allowance with retryable: true and advice to wait for an active analysis to complete. Two consecutive calls with identical authenticated synthetic inventory Evidence returned the same refusal, with no provider launches. A smaller control succeeded on the same connection.

This is an executed production MCP handler test using the SDK's in-memory transport and repository fixtures. No native analysis engine was launched. The oversized refusal is intentional; the incorrect retry flag and recovery action are the defect.

Reproduction

With dependencies installed at the source revision below, save this test as tests/boundary/mcp/nativeMatchingBudget.test.ts and run:

node node_modules/vitest/vitest.mjs run tests/boundary/mcp/nativeMatchingBudget.test.ts --maxWorkers=1

The following matching case was executed as part of one focused test file, then this exact block was extracted and replayed independently with one test passing. Its assertions characterize the current incorrect result. The inventory with a 24 MiB function-name string passes Evidence authentication before the MCP request. The admission algorithm charges strings at 64 + 4 * length, so that string plus its enclosing record exceeds the conservative 96 MiB representation budget; the serialized request need not itself exceed 96 MiB.

import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
import { expect, it } from "vitest";
import { createTestBinarySession } from "../../fixtures/binarySession.js";
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
import { nativeMatchingInventory } from "../../fixtures/nativeFunctionMatching.js";
import { createServer } from "../../../src/server/createServer.js";
import { parseEvidence } from "../../../src/domain/evidence.js";

it("returns retryable capacity for a fixed native matching representation allowance", async () => {
  let providerLaunches = 0;
  const session = createTestBinarySession(() => {
    providerLaunches++;
    throw new Error("No provider should start");
  });
  const server = createServer({ kind: "session", session });
  const client = new Client({ name: "native-budget-repro", version: "1" });
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
  const left = nativeMatchingInventory("1", [{ address: "0x10", value: "main" }]);
  const right = nativeMatchingInventory("2", [{ address: "0x20", value: "main" }]);
  const large = nativeMatchingInventory("1", [{ address: "0x10", value: "x".repeat(24 * 1024 * 1024) }]);
  expect(parseEvidence(large).evidence_id).toBe(large.evidence_id);
  try {
    await server.connect(serverTransport);
    await client.connect(clientTransport);
    for (let attempt = 1; attempt <= 2; attempt++) {
      const result = await client.callTool({ name: "match_native_functions", arguments: { left: large, right, detail: "complete" } });
      const diagnostic = parseMcpToolError(result);
      console.log(JSON.stringify({ attempt, diagnostic }));
      expect(diagnostic.error).toMatchObject({
        code: "resource_constraint", category: "resource_constraint", retryable: true,
        details: { resource: "capacity", reported_limits: { maximum_bytes: 100663296 } },
        remediation: { action: "Wait for an active analysis to complete, then retry within the reported admission limits." },
      });
    }
    const control = await client.callTool({ name: "match_native_functions", arguments: { left, right, detail: "complete" } });
    expect(control.isError).not.toBe(true);
    expect(parseEvidence(control.structuredContent).normalized_result).toMatchObject({ summary: { matched: 1 } });
    expect(providerLaunches).toBe(0);
    await client.ping();
    console.log(JSON.stringify({ control: "matched: 1", providerLaunches, ping: "ok" }));
  } finally {
    await client.close();
    await server.close();
    await session.close();
  }
});

Expected behavior

Keep refusing the oversized request and preserve its reported limit. Return retryable: false with recovery explaining that this is a fixed representation allowance and that waiting or repeating unchanged observations cannot increase it. Selecting different complete observations may help when that preserves the required analysis; silently dropping required observations would change coverage.

The resource error contract defines retryability by whether retrying the same request can succeed. The fixed-allowance helper explicitly distinguishes fixed byte allowances from occupied admission capacity. The matching guide documents this 96 MiB representation bound and says no worker/provider concurrency is introduced.

Impact and scope

P2: the machine-readable flag can direct callers to repeat a deterministic refusal that waiting cannot resolve. This was observed twice with unchanged inline Evidence through MCP. No automatic retry loop, service outage, data loss, or incorrect successful match was observed. Callers can recognize the reason and numeric limit and stop retrying. A smaller inventory succeeds, but that does not prove a way to preserve all facts from an oversized input.

Frequency with real provider inventories is unknown. The CLI uses the same admission service by source inspection; CLI behavior, portable bundle inputs, retained references, other platforms, and real-provider-produced large inputs were not separately executed.

REA version

rea-agents 6.7.0; source revision 52415975942a4b9c53a817915b313ccc404f280e.

Runtime and platform

macOS 27.0.1 arm64; Node.js v26.12.0; locked Vitest 5.0.3. The repository's exact Node 24.18.0 pin was not separately exercised.

Target and artifact context

Authenticated synthetic list_procedures Evidence from the repository's native matching fixture. The oversized left record contains one function whose reported name is 24 * 1024 * 1024 characters. The right inventory and smaller control each contain one ordinary named function. No target binary was opened or executed.

Interface

MCP

Earliest failure stage

Request parsing or validation

Relevant output or evidence

Both calls returned isError: true and the following canonical diagnostic as text content:

{
  "error": {
    "code": "resource_constraint",
    "category": "resource_constraint",
    "message": "Native function matching exceeds representation capacity",
    "retryable": true,
    "remediation": {
      "action": "Wait for an active analysis to complete, then retry within the reported admission limits."
    },
    "details": {
      "operation": "match_native_functions",
      "resource": "capacity",
      "reason": "Native function matching exceeds representation capacity",
      "reported_limits": { "maximum_bytes": 100663296 }
    }
  }
}

The smaller control returned successful matching Evidence with summary.matched: 1. The provider-launch count remained zero, ping succeeded, and the client, server and session closed. The full focused run passed three characterization cases, including two unrelated attached-provider controls; the matching case above includes both oversized calls and the successful smaller control.

Typed outcome or recovery expected

Keep resource_constraint, details.resource: "capacity", and details.reported_limits.maximum_bytes: 100663296. Set retryable: false and explain the deterministic representation limit. Genuine occupied-capacity admission elsewhere should retain truthful retry semantics.

Analysis or suspected cause

Source-derived: the matching service creates a fresh allowance for each call and admits selected records before recursive parsing. The allowance owner throws a capacity error without retryability or remediation options. Shared projection defaults an unflagged capacity constraint to retryable, and presentation supplies the wait-and-retry action.

The registered MCP handler does use ordinary analysis admission, but that does not turn this per-request representation allowance into an occupied slot that another request can release. An explicit fixed-allowance override at the producing boundary can preserve the distinction without changing temporary capacity behavior globally.

Additional context

Related reports checked: #5950 concerns malformed inline Evidence losing field paths; #5961 concerns authentic Evidence in the wrong request role. Both have different producing branches. #5506 concerns WASM comparison's separate 32 MiB budget, and #5628 concerns WASM decoder budgeting. PR #5740 explicitly identifies the NativeMatchingAdmission retryability issue as excluded from its changes and requiring a separate fix.

Fetched upstream main at 6d356b84344659085f2b912857b5e27fe0160c9b: the matching admission, service, contract, and relevant retryability/recovery lines are unchanged. That newer source was compared, not executed. No regression range was established.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6035 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Native analysis or decompilation ### Observed behavior The registered `match_native_functions` MCP tool refuses observations above its fixed 96 MiB representation allowance with `retryable: true` and advice to wait for an active analysis to complete. Two consecutive calls with identical authenticated synthetic inventory Evidence returned the same refusal, with no provider launches. A smaller control succeeded on the same connection. This is an executed production MCP handler test using the SDK's in-memory transport and repository fixtures. No native analysis engine was launched. The oversized refusal is intentional; the incorrect retry flag and recovery action are the defect. ### Reproduction With dependencies installed at the source revision below, save this test as `tests/boundary/mcp/nativeMatchingBudget.test.ts` and run: ```sh node node_modules/vitest/vitest.mjs run tests/boundary/mcp/nativeMatchingBudget.test.ts --maxWorkers=1 ``` The following matching case was executed as part of one focused test file, then this exact block was extracted and replayed independently with one test passing. Its assertions characterize the current incorrect result. The inventory with a 24 MiB function-name string passes Evidence authentication before the MCP request. The admission algorithm charges strings at `64 + 4 * length`, so that string plus its enclosing record exceeds the conservative 96 MiB representation budget; the serialized request need not itself exceed 96 MiB. ```ts import { Client, InMemoryTransport } from "@modelcontextprotocol/client"; import { expect, it } from "vitest"; import { createTestBinarySession } from "../../fixtures/binarySession.js"; import { parseMcpToolError } from "../../fixtures/mcpToolError.js"; import { nativeMatchingInventory } from "../../fixtures/nativeFunctionMatching.js"; import { createServer } from "../../../src/server/createServer.js"; import { parseEvidence } from "../../../src/domain/evidence.js"; it("returns retryable capacity for a fixed native matching representation allowance", async () => { let providerLaunches = 0; const session = createTestBinarySession(() => { providerLaunches++; throw new Error("No provider should start"); }); const server = createServer({ kind: "session", session }); const client = new Client({ name: "native-budget-repro", version: "1" }); const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); const left = nativeMatchingInventory("1", [{ address: "0x10", value: "main" }]); const right = nativeMatchingInventory("2", [{ address: "0x20", value: "main" }]); const large = nativeMatchingInventory("1", [{ address: "0x10", value: "x".repeat(24 * 1024 * 1024) }]); expect(parseEvidence(large).evidence_id).toBe(large.evidence_id); try { await server.connect(serverTransport); await client.connect(clientTransport); for (let attempt = 1; attempt <= 2; attempt++) { const result = await client.callTool({ name: "match_native_functions", arguments: { left: large, right, detail: "complete" } }); const diagnostic = parseMcpToolError(result); console.log(JSON.stringify({ attempt, diagnostic })); expect(diagnostic.error).toMatchObject({ code: "resource_constraint", category: "resource_constraint", retryable: true, details: { resource: "capacity", reported_limits: { maximum_bytes: 100663296 } }, remediation: { action: "Wait for an active analysis to complete, then retry within the reported admission limits." }, }); } const control = await client.callTool({ name: "match_native_functions", arguments: { left, right, detail: "complete" } }); expect(control.isError).not.toBe(true); expect(parseEvidence(control.structuredContent).normalized_result).toMatchObject({ summary: { matched: 1 } }); expect(providerLaunches).toBe(0); await client.ping(); console.log(JSON.stringify({ control: "matched: 1", providerLaunches, ping: "ok" })); } finally { await client.close(); await server.close(); await session.close(); } }); ``` ### Expected behavior Keep refusing the oversized request and preserve its reported limit. Return `retryable: false` with recovery explaining that this is a fixed representation allowance and that waiting or repeating unchanged observations cannot increase it. Selecting different complete observations may help when that preserves the required analysis; silently dropping required observations would change coverage. The [resource error contract](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/analysisErrorCore.ts#L32-L55) defines retryability by whether retrying the same request can succeed. The [fixed-allowance helper](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/fixedAllowanceRemediation.ts#L15-L49) explicitly distinguishes fixed byte allowances from occupied admission capacity. The [matching guide](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/docs/native-function-matching.md#L110-L112) documents this 96 MiB representation bound and says no worker/provider concurrency is introduced. ### Impact and scope P2: the machine-readable flag can direct callers to repeat a deterministic refusal that waiting cannot resolve. This was observed twice with unchanged inline Evidence through MCP. No automatic retry loop, service outage, data loss, or incorrect successful match was observed. Callers can recognize the reason and numeric limit and stop retrying. A smaller inventory succeeds, but that does not prove a way to preserve all facts from an oversized input. Frequency with real provider inventories is unknown. The CLI uses the same admission service by source inspection; CLI behavior, portable bundle inputs, retained references, other platforms, and real-provider-produced large inputs were not separately executed. ### REA version `rea-agents` 6.7.0; source revision `52415975942a4b9c53a817915b313ccc404f280e`. ### Runtime and platform macOS 27.0.1 arm64; Node.js v26.12.0; locked Vitest 5.0.3. The repository's exact Node 24.18.0 pin was not separately exercised. ### Target and artifact context Authenticated synthetic `list_procedures` Evidence from the repository's native matching fixture. The oversized left record contains one function whose reported name is 24 * 1024 * 1024 characters. The right inventory and smaller control each contain one ordinary named function. No target binary was opened or executed. ### Interface MCP ### Earliest failure stage Request parsing or validation ### Relevant output or evidence Both calls returned `isError: true` and the following canonical diagnostic as text content: ```json { "error": { "code": "resource_constraint", "category": "resource_constraint", "message": "Native function matching exceeds representation capacity", "retryable": true, "remediation": { "action": "Wait for an active analysis to complete, then retry within the reported admission limits." }, "details": { "operation": "match_native_functions", "resource": "capacity", "reason": "Native function matching exceeds representation capacity", "reported_limits": { "maximum_bytes": 100663296 } } } } ``` The smaller control returned successful matching Evidence with `summary.matched: 1`. The provider-launch count remained zero, `ping` succeeded, and the client, server and session closed. The full focused run passed three characterization cases, including two unrelated attached-provider controls; the matching case above includes both oversized calls and the successful smaller control. ### Typed outcome or recovery expected Keep `resource_constraint`, `details.resource: "capacity"`, and `details.reported_limits.maximum_bytes: 100663296`. Set `retryable: false` and explain the deterministic representation limit. Genuine occupied-capacity admission elsewhere should retain truthful retry semantics. ### Analysis or suspected cause Source-derived: [the matching service](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/application/native/NativeFunctionMatchingService.ts#L36-L81) creates a fresh allowance for each call and admits selected records before recursive parsing. [The allowance owner](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/application/native/NativeMatchingAdmission.ts#L4-L23) throws a capacity error without retryability or remediation options. [Shared projection](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/analysisErrorProjection.ts#L109-L123) defaults an unflagged capacity constraint to retryable, and [presentation](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/analysisErrorPresentation.ts#L63-L78) supplies the wait-and-retry action. The [registered MCP handler](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/server/registerNativeFunctionMatchingTool.ts#L26-L58) does use ordinary analysis admission, but that does not turn this per-request representation allowance into an occupied slot that another request can release. An explicit fixed-allowance override at the producing boundary can preserve the distinction without changing temporary capacity behavior globally. ### Additional context Related reports checked: #5950 concerns malformed inline Evidence losing field paths; #5961 concerns authentic Evidence in the wrong request role. Both have different producing branches. #5506 concerns WASM comparison's separate 32 MiB budget, and #5628 concerns WASM decoder budgeting. PR #5740 explicitly identifies the `NativeMatchingAdmission` retryability issue as excluded from its changes and requiring a separate fix. Fetched upstream main at `6d356b84344659085f2b912857b5e27fe0160c9b`: the matching admission, service, contract, and relevant retryability/recovery lines are unchanged. That newer source was compared, not executed. No regression range was established.
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

Checked the reported fix on main at d87e73f2db.

The fixed 96 MiB native-matching representation allowance now produces a non-retryable capacity refusal. Its recovery explains that waiting or repeating an unchanged request cannot raise the allowance. The production allowance-error projection was also checked directly with a 100663297-byte checkpoint, without making a large allocation.

Implementation: NativeMatchingAdmission.ts, nativeDeadlines.ts.

The relevant regression checks in nativeFunctionMatching.test.ts, NativeFunctionMatchingService.test.ts passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.

<!-- gh-comment-id:6380715727 --> @coygeek commented on GitHub (Oct 24, 2026): Checked the reported fix on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. The fixed 96 MiB native-matching representation allowance now produces a non-retryable capacity refusal. Its recovery explains that waiting or repeating an unchanged request cannot raise the allowance. The production allowance-error projection was also checked directly with a 100663297-byte checkpoint, without making a large allocation. Implementation: [NativeMatchingAdmission.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/application/native/NativeMatchingAdmission.ts#L18), [nativeDeadlines.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/native/nativeDeadlines.ts#L100). The relevant regression checks in [nativeFunctionMatching.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/tests/acceptance/tools/nativeFunctionMatching.test.ts), [NativeFunctionMatchingService.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/application/native/NativeFunctionMatchingService.test.ts) passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.
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#3684
No description provided.