mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
fix(evm): report the carrier input allowance as a file-size constraint (#6414)
The EVM interface failure translator grouped the stable reader's input limit with path and format refusals, so a carrier over the fixed 4 MiB input allowance surfaced as a non-retryable invalid_request whose only record of the allowance was issue text pointing at the input schema. The same refusal now returns a non-retryable resource_constraint with resource file-size and reported_limits.maximum_input_bytes, following the WABT input translator, while path and format reader refusals stay input errors. The reply-size write failure keeps its own branch. Fixes #6053
This commit is contained in:
1 parent
37e92e430b
commit
5d81363391
3 files changed
+76
-11
No files matched your search
@@ -110,7 +110,9 @@ address-space limit and 30 CPU seconds. The worker command deadline is
|
||||
`timeout_ms` (default 30000 ms, integer 1 through 2147483647) on the next call,
|
||||
or `--timeout-ms` on `rea inspect-evm-interface`. Omission keeps 30000 ms.
|
||||
Those byte, address-space, and CPU ceilings have no runtime override, and
|
||||
`timeout_ms` does not raise them. Virtual
|
||||
`timeout_ms` does not raise them. An oversized selected carrier returns a
|
||||
non-retryable `resource_constraint` with resource `file-size` and its fixed
|
||||
input allowance in `reported_limits.maximum_input_bytes`. Virtual
|
||||
address space differs from resident memory. The worker retains inherited tighter
|
||||
soft/hard limits, reports its configured soft limits inline and leaves hard limits
|
||||
unchanged. WASM trap reservations are disabled
|
||||
|
||||
@@ -2,6 +2,7 @@ import { expect, it } from "vitest";
|
||||
import { evmInterfaceFailure } from "./EvmoleFailures.js";
|
||||
import { OwnedCommandFailure } from "../process/OwnedCommand.js";
|
||||
import { AnalysisCancelledError } from "../domain/analysisErrorCore.js";
|
||||
import { ArtifactReaderFailure } from "../artifacts/ArtifactReader.js";
|
||||
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
||||
import {
|
||||
DEFAULT_EVM_TIMEOUT_MS,
|
||||
@@ -106,6 +107,60 @@ it("says a CPU signal has no runtime override and timeout_ms does not raise it",
|
||||
);
|
||||
});
|
||||
|
||||
it("reports the fixed carrier input allowance as a resource constraint", () => {
|
||||
const source = new ArtifactReaderFailure(
|
||||
"limit",
|
||||
"Selected artifact exceeds 4194304 bytes: /selected.bin",
|
||||
{ boundary: "file-size" },
|
||||
);
|
||||
const projected = projectAnalysisError(
|
||||
evmInterfaceFailure(source, "artifact-read", "/selected.bin"),
|
||||
);
|
||||
expect(projected).toMatchObject({
|
||||
code: "resource_constraint",
|
||||
category: "resource_constraint",
|
||||
retryable: false,
|
||||
details: {
|
||||
operation: "inspect_evm_interface",
|
||||
resource: "file-size",
|
||||
reported_limits: { maximum_input_bytes: 4194304 },
|
||||
},
|
||||
});
|
||||
expect(projected.message).toContain("4194304");
|
||||
expect(projected.message).toContain("no runtime override");
|
||||
expect(projected.message).toContain("timeout_ms does not raise it");
|
||||
expect(projected.remediation.action).toContain("4194304");
|
||||
expect(projected.remediation.action).toContain("no runtime override");
|
||||
expect(projected.remediation.action).toContain("Do not truncate");
|
||||
expect(projected.remediation.action).toContain(
|
||||
"repeating the unchanged request fails the same way",
|
||||
);
|
||||
expect(projected.recovery).toBeUndefined();
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
reason: "path" as const,
|
||||
message: "Expected a regular file without a symlink: /selected.bin",
|
||||
},
|
||||
{ reason: "format" as const, message: "Unsupported carrier format." },
|
||||
])("keeps reader $reason refusals as invalid input", ({ reason, message }) => {
|
||||
const projected = projectAnalysisError(
|
||||
evmInterfaceFailure(
|
||||
new ArtifactReaderFailure(reason, message),
|
||||
"artifact-read",
|
||||
"/selected.bin",
|
||||
),
|
||||
);
|
||||
expect(projected).toMatchObject({
|
||||
code: "invalid_request",
|
||||
category: "invalid_input",
|
||||
details: {
|
||||
issues: [{ path: ["path"], reason: "invalid_format", message }],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ failure: "process" as const, cleanup: null, tag: "ProviderAdapterError" },
|
||||
{ failure: "timeout" as const, cleanup: null, tag: "AnalysisTimeoutError" },
|
||||
|
||||
+18
-10
@@ -173,28 +173,36 @@ export const evmInterfaceFailure = (
|
||||
);
|
||||
if (
|
||||
phase === "artifact-read" &&
|
||||
["limit", "path", "format"].includes(cause.reason)
|
||||
) {
|
||||
const inputLimited = cause.reason === "limit";
|
||||
const message = inputLimited
|
||||
? `${cause.message} The input allowance is ${String(EVM_INTERFACE_LIMITS.inputBytes)} bytes. This release has no runtime override. timeout_ms does not raise it.`
|
||||
: cause.message;
|
||||
cause.reason === "limit" &&
|
||||
cause.boundary === "file-size"
|
||||
)
|
||||
return new AnalysisResourceConstraintError(
|
||||
"inspect_evm_interface",
|
||||
"file-size",
|
||||
`${cause.message} The input allowance is ${String(EVM_INTERFACE_LIMITS.inputBytes)} bytes. This release has no runtime override. timeout_ms does not raise it.`,
|
||||
{ maximum_input_bytes: EVM_INTERFACE_LIMITS.inputBytes },
|
||||
{
|
||||
cause,
|
||||
...cleanupOptions,
|
||||
retryable: false,
|
||||
remediationAction: `Select a suitable complete carrier within the ${String(EVM_INTERFACE_LIMITS.inputBytes)}-byte input allowance. This release has no runtime override. timeout_ms does not raise it, and reply or diagnostic allowances do not admit a larger carrier. Do not truncate required bytes; repeating the unchanged request fails the same way.`,
|
||||
},
|
||||
);
|
||||
if (phase === "artifact-read" && ["path", "format"].includes(cause.reason))
|
||||
return new AnalysisInputError(
|
||||
"inspect_evm_interface",
|
||||
{
|
||||
cause,
|
||||
...cleanupOptions,
|
||||
...(inputLimited ? { userMessage: message } : {}),
|
||||
},
|
||||
[
|
||||
{
|
||||
path: ["path"],
|
||||
reason: inputLimited ? "out_of_range" : "invalid_format",
|
||||
message,
|
||||
reason: "invalid_format",
|
||||
message: cause.message,
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
if (phase === "artifact-read" && cause instanceof Error && "code" in cause) {
|
||||
if (cause.code === "EACCES" || cause.code === "EPERM")
|
||||
|
||||
Reference in new issue
Block a user