[GH-ISSUE #5504] [Bug] Rust compiler-hash capacity refusals incorrectly tell callers to retry #3350

Closed
opened 2026-10-31 00:39:25 +00:00 by bsakaguchi · 1 comment
Owner

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

Before submitting

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

Area

CLI

Observed behavior

node scripts/rea.mjs inspect-rust-binary <artifact> --json correctly refuses an ELF containing 65 distinct read-only compiler paths, but returns retryable: true and tells the caller to wait for active analysis. Repeating the unchanged request fails again because the fixed capacity is 64 hashes. The production MCP tool inspect_rust_binary returns the same incorrect retry flag and recovery advice.

Reproduction

From the baseline source checkout with dependencies and the built runtime prepared, run the following. An independent verifier executed this exact script. It generates the existing 65-hash synthetic ELF fixture, clears only its final 48-byte path for a 64-hash control, isolates configuration and state, and inspects overflow/control/overflow. No Rust compiler or cargo-auditable installation is needed.

python3 - <<'PYTHON'
import hashlib, json, os, pathlib, subprocess, tempfile
work = pathlib.Path(tempfile.mkdtemp(prefix='rea-rust-hash-check-', dir=os.environ.get('VERIFY_WORK_ROOT')))
env = {'PATH': os.environ['PATH']}
for key, name in [('HOME', 'home'), ('XDG_CONFIG_HOME', 'config'),
                  ('XDG_DATA_HOME', 'data'), ('XDG_CACHE_HOME', 'cache'),
                  ('XDG_STATE_HOME', 'state'), ('TMPDIR', 'temp')]:
    folder = work / name
    folder.mkdir()
    env[key] = str(folder)
fixture = '''
import { createRustHashOverflowImage } from './tests/fixtures/rust/image.ts';
import { writeFileSync } from 'node:fs';
import { join } from 'node:path';
const bytes = createRustHashOverflowImage();
writeFileSync(join(process.argv[1], '65.elf'), bytes);
bytes.fill(0, 800 + 64 * 48, 800 + 65 * 48);
writeFileSync(join(process.argv[1], '64.elf'), bytes);
'''
subprocess.run(['node', '--input-type=module', '-e', fixture, str(work)],
               env=env, check=True, capture_output=True, text=True)
for label, count in [('overflow', 65), ('control', 64), ('repeat', 65)]:
    artifact = work / (str(count) + '.elf')
    digest = hashlib.sha256(artifact.read_bytes()).hexdigest()
    result = subprocess.run(['node', 'scripts/rea.mjs', 'inspect-rust-binary',
                             str(artifact), '--json'], env=env,
                            capture_output=True, text=True, timeout=45)
    (work / (label + '.json')).write_text(result.stdout)
    (work / (label + '.stderr')).write_text(result.stderr)
    value = json.loads(result.stdout)
    report = value.get('normalized_result')
    print(json.dumps({'case': label, 'exit': result.returncode,
                      'stderr': result.stderr, 'bytes': artifact.stat().st_size,
                      'unchanged': digest == hashlib.sha256(artifact.read_bytes()).hexdigest(),
                      'result': {'hashes': len(report['rustc_commit']['hashes']),
                                 'auditable_metadata': report['auditable_metadata']}
                                if report else {key: value[key] for key in
                                    ['code', 'category', 'retryable', 'remediation', 'details']}}))
PYTHON

The verifier observed both 65-hash CLI refusals and the passing 64-hash control, then replayed the exact script with the same results. Input digest comparisons stayed unchanged. The verifier also started node scripts/rea.mjs mcp, initialized a production stdio connection, and called inspect_rust_binary with path set to each generated artifact's absolute filesystem path in overflow/control/overflow order. Both MCP overflow calls returned isError: true; the control succeeded. No binary target was opened.

Expected behavior

Keep the fixed compiler-path refusal, but return retryable: false for this deterministic request. Recovery guidance should explain the fixed count and that waiting for another analysis cannot admit the unchanged artifact.

src/domain/analysisErrorCore.ts:36-41 defines retryability as whether the same request can succeed and excludes deterministic bounds. docs/rust-binaries.md:3-6,55-56,69-72 documents offline inspection without active analysis or a Rust installation, permits absent dependency metadata, and distinguishes compiler-hash capacity from byte limits. This expectation follows from those source contracts.

Impact and scope

P2. The incorrect machine-readable flag can direct clients to retry a request that cannot recover by waiting. Both tested interfaces produced consistent refusals around a passing threshold control. The observed input was a synthetic ELF with 65 distinct compiler paths. No uncontrolled client retry loop, incorrect successful report, data loss, or broader prevalence was observed.

A caller can recognize maximum_rustc_hashes and stop retrying the unchanged request despite its flag. The 64-hash control demonstrates a supported narrower input, not recovery of the original complete report. No public option to raise this fixed count was found. Installed-package and other-platform behavior were not tested.

REA version

6.7.0; source commit 074768901f3cdc8205b61773fb1faf6f6c7b3ca8 with the prepared built CLI/MCP runtime.

Runtime and platform

Node.js v26.12.0; Darwin arm64. No external analysis engine was used.

Target and artifact context

4,096-byte synthetic ELF from tests/fixtures/rust/image.ts:250-263, with 65 distinct read-only compiler paths. The 64-hash control keeps the same container and section sizes and has supported null dependency metadata. Inspection did not execute or alter either input.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

Both initial 65-hash CLI runs and both exact-script replay runs exited 1 with empty stderr and the following diagnostic fields:

{
  "code": "resource_constraint",
  "category": "resource_constraint",
  "retryable": true,
  "remediation": {
    "action": "Wait for an active analysis to complete, then retry within the reported admission limits."
  },
  "details": {
    "operation": "inspect_rust_binary",
    "resource": "capacity",
    "reported_limits": { "maximum_rustc_hashes": 64 }
  }
}

The message correctly identifies more than 64 distinct rustc commit paths. Each CLI control exited 0 with exactly 64 hashes and auditable_metadata: null. Both MCP overflow results had isError: true with the same retry flag and action; the intervening MCP control returned 64 hashes. These are the independent verifier's executed production-interface observations.

Typed outcome or recovery expected

Preserve resource_constraint, maximum_rustc_hashes: 64, and the intentional complete-report refusal, while returning retryable: false and count-specific recovery guidance. Genuine transient admission failures should retain appropriate retry semantics. No particular resource enum change is required.

Analysis or suspected cause

Source-derived reasoning at the tested revision: src/rust/RustBinaryImage.ts:155-163 refuses the 65th distinct hash. src/rust/RustBinaryProvider.ts:181-190 maps that refusal without retryability or recovery overrides. src/domain/analysisErrorProjection.ts:109-115 defaults capacity to retryable, and src/domain/analysisErrorPresentation.ts:67-72 supplies active-analysis admission advice.

Additional context

Prepared with AI assistance and independently reproduced through both supported CLI and production MCP. The submission check fetched upstream main at c374c2fe7290b914d73d9e27af38b498753ec593 and found no later commits touching the cited contract or failure-source files. Current-main applicability was source-checked, not separately runtime-reproduced there. No first-bad revision was established.

Related but separate: #5405 covers Rust/IL2CPP input-size recovery with correct non-retryable status; #5459 covers analysis-view representation budgets; #5460 covers IL2CPP complete-report budgets; #5456 covers SQLite worker replies. Each concerns a different producer and can be restored independently of this compiler-path retry defect.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5504 ### 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 CLI ### Observed behavior `node scripts/rea.mjs inspect-rust-binary <artifact> --json` correctly refuses an ELF containing 65 distinct read-only compiler paths, but returns `retryable: true` and tells the caller to wait for active analysis. Repeating the unchanged request fails again because the fixed capacity is 64 hashes. The production MCP tool `inspect_rust_binary` returns the same incorrect retry flag and recovery advice. ### Reproduction From the baseline source checkout with dependencies and the built runtime prepared, run the following. An independent verifier executed this exact script. It generates the existing 65-hash synthetic ELF fixture, clears only its final 48-byte path for a 64-hash control, isolates configuration and state, and inspects overflow/control/overflow. No Rust compiler or cargo-auditable installation is needed. ```sh python3 - <<'PYTHON' import hashlib, json, os, pathlib, subprocess, tempfile work = pathlib.Path(tempfile.mkdtemp(prefix='rea-rust-hash-check-', dir=os.environ.get('VERIFY_WORK_ROOT'))) env = {'PATH': os.environ['PATH']} for key, name in [('HOME', 'home'), ('XDG_CONFIG_HOME', 'config'), ('XDG_DATA_HOME', 'data'), ('XDG_CACHE_HOME', 'cache'), ('XDG_STATE_HOME', 'state'), ('TMPDIR', 'temp')]: folder = work / name folder.mkdir() env[key] = str(folder) fixture = ''' import { createRustHashOverflowImage } from './tests/fixtures/rust/image.ts'; import { writeFileSync } from 'node:fs'; import { join } from 'node:path'; const bytes = createRustHashOverflowImage(); writeFileSync(join(process.argv[1], '65.elf'), bytes); bytes.fill(0, 800 + 64 * 48, 800 + 65 * 48); writeFileSync(join(process.argv[1], '64.elf'), bytes); ''' subprocess.run(['node', '--input-type=module', '-e', fixture, str(work)], env=env, check=True, capture_output=True, text=True) for label, count in [('overflow', 65), ('control', 64), ('repeat', 65)]: artifact = work / (str(count) + '.elf') digest = hashlib.sha256(artifact.read_bytes()).hexdigest() result = subprocess.run(['node', 'scripts/rea.mjs', 'inspect-rust-binary', str(artifact), '--json'], env=env, capture_output=True, text=True, timeout=45) (work / (label + '.json')).write_text(result.stdout) (work / (label + '.stderr')).write_text(result.stderr) value = json.loads(result.stdout) report = value.get('normalized_result') print(json.dumps({'case': label, 'exit': result.returncode, 'stderr': result.stderr, 'bytes': artifact.stat().st_size, 'unchanged': digest == hashlib.sha256(artifact.read_bytes()).hexdigest(), 'result': {'hashes': len(report['rustc_commit']['hashes']), 'auditable_metadata': report['auditable_metadata']} if report else {key: value[key] for key in ['code', 'category', 'retryable', 'remediation', 'details']}})) PYTHON ``` The verifier observed both 65-hash CLI refusals and the passing 64-hash control, then replayed the exact script with the same results. Input digest comparisons stayed unchanged. The verifier also started `node scripts/rea.mjs mcp`, initialized a production stdio connection, and called `inspect_rust_binary` with `path` set to each generated artifact's absolute filesystem path in overflow/control/overflow order. Both MCP overflow calls returned `isError: true`; the control succeeded. No binary target was opened. ### Expected behavior Keep the fixed compiler-path refusal, but return `retryable: false` for this deterministic request. Recovery guidance should explain the fixed count and that waiting for another analysis cannot admit the unchanged artifact. `src/domain/analysisErrorCore.ts:36-41` defines retryability as whether the same request can succeed and excludes deterministic bounds. `docs/rust-binaries.md:3-6,55-56,69-72` documents offline inspection without active analysis or a Rust installation, permits absent dependency metadata, and distinguishes compiler-hash capacity from byte limits. This expectation follows from those source contracts. ### Impact and scope P2. The incorrect machine-readable flag can direct clients to retry a request that cannot recover by waiting. Both tested interfaces produced consistent refusals around a passing threshold control. The observed input was a synthetic ELF with 65 distinct compiler paths. No uncontrolled client retry loop, incorrect successful report, data loss, or broader prevalence was observed. A caller can recognize `maximum_rustc_hashes` and stop retrying the unchanged request despite its flag. The 64-hash control demonstrates a supported narrower input, not recovery of the original complete report. No public option to raise this fixed count was found. Installed-package and other-platform behavior were not tested. ### REA version 6.7.0; source commit `074768901f3cdc8205b61773fb1faf6f6c7b3ca8` with the prepared built CLI/MCP runtime. ### Runtime and platform Node.js v26.12.0; Darwin arm64. No external analysis engine was used. ### Target and artifact context 4,096-byte synthetic ELF from `tests/fixtures/rust/image.ts:250-263`, with 65 distinct read-only compiler paths. The 64-hash control keeps the same container and section sizes and has supported null dependency metadata. Inspection did not execute or alter either input. ### Interface CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence Both initial 65-hash CLI runs and both exact-script replay runs exited 1 with empty stderr and the following diagnostic fields: ```json { "code": "resource_constraint", "category": "resource_constraint", "retryable": true, "remediation": { "action": "Wait for an active analysis to complete, then retry within the reported admission limits." }, "details": { "operation": "inspect_rust_binary", "resource": "capacity", "reported_limits": { "maximum_rustc_hashes": 64 } } } ``` The message correctly identifies more than 64 distinct rustc commit paths. Each CLI control exited 0 with exactly 64 hashes and `auditable_metadata: null`. Both MCP overflow results had `isError: true` with the same retry flag and action; the intervening MCP control returned 64 hashes. These are the independent verifier's executed production-interface observations. ### Typed outcome or recovery expected Preserve `resource_constraint`, `maximum_rustc_hashes: 64`, and the intentional complete-report refusal, while returning `retryable: false` and count-specific recovery guidance. Genuine transient admission failures should retain appropriate retry semantics. No particular resource enum change is required. ### Analysis or suspected cause Source-derived reasoning at the tested revision: `src/rust/RustBinaryImage.ts:155-163` refuses the 65th distinct hash. `src/rust/RustBinaryProvider.ts:181-190` maps that refusal without retryability or recovery overrides. `src/domain/analysisErrorProjection.ts:109-115` defaults capacity to retryable, and `src/domain/analysisErrorPresentation.ts:67-72` supplies active-analysis admission advice. ### Additional context Prepared with AI assistance and independently reproduced through both supported CLI and production MCP. The submission check fetched upstream main at `c374c2fe7290b914d73d9e27af38b498753ec593` and found no later commits touching the cited contract or failure-source files. Current-main applicability was source-checked, not separately runtime-reproduced there. No first-bad revision was established. Related but separate: #5405 covers Rust/IL2CPP input-size recovery with correct non-retryable status; #5459 covers analysis-view representation budgets; #5460 covers IL2CPP complete-report budgets; #5456 covers SQLite worker replies. Each concerns a different producer and can be restored independently of this compiler-path retry defect.
Author
Owner

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

Closing as fixed on main by github.com/morluto/rea@ef59174c1e, retained at 67a6477f01.

Rust compiler-hash capacity errors now identify the fixed rustc commit-path count allowance and are non-retryable. The provider-boundary overflow regression asserts the 64-path limit, fixed-count guidance and absence of wait advice.

This audit inspected the current producer, regression source, issue discussion and main ancestry. Tests/runtime reproductions were not rerun, and no additional real-provider or platform coverage is implied.

<!-- gh-comment-id:6324324425 --> @morluto commented on GitHub (Oct 21, 2026): Closing as fixed on main by https://github.com/morluto/rea/commit/ef59174c1edd6ed9adb0722b86a5b9a57217e310, retained at 67a6477f011ae358fc333e5a6e26c86d932654ed. Rust compiler-hash capacity errors now identify the fixed rustc commit-path count allowance and are non-retryable. The provider-boundary overflow regression asserts the 64-path limit, fixed-count guidance and absence of wait advice. This audit inspected the current producer, regression source, issue discussion and main ancestry. Tests/runtime reproductions were not rerun, and no additional real-provider or platform coverage is implied.
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#3350
No description provided.