fix(agents): surface disk full session write errors (#61264)

This commit is contained in:
Vincent Koc
2026-04-05 10:09:42 +01:00
committed by GitHub
parent ef3a185225
commit d5cde2171b
4 changed files with 48 additions and 0 deletions

View File

@@ -134,6 +134,7 @@ Docs: https://docs.openclaw.ai
- Exec/heartbeat: use the canonical `exec-event` wake reason for `notifyOnExit` so background exec completions still trigger follow-up turns when `HEARTBEAT.md` is empty or comments-only. (#41479) Thanks @rstar327.
- Heartbeat: skip wake delivery when the target session lane is already busy so the pending event is retried instead of getting drained too early. (#40526) Thanks @lucky7323.
- Plugin SDK/context engines: export the missing context-engine result and subagent lifecycle types from `openclaw/plugin-sdk` so context engine plugins can type `ContextEngine` implementations without local workarounds. (#61251) Thanks @DaevMithran.
- Agents/errors: surface an explicit disk-full message when local session or transcript writes fail with `ENOSPC`/`disk full`, so those runs stop degrading into opaque `NO_REPLY`-style failures. Thanks @vincentkoc.
## 2026.4.2

View File

@@ -191,6 +191,16 @@ describe("formatAssistantErrorText", () => {
);
});
it.each(["disk full", "ENOSPC: no space left on device, write"])(
"returns a friendly disk-space message for %s",
(errorMessage) => {
const msg = makeAssistantError(errorMessage);
expect(formatAssistantErrorText(msg)).toBe(
"OpenClaw could not write local session data because the disk is full. Free some disk space and try again.",
);
},
);
it("returns a DNS-specific message for provider lookup failures", () => {
const msg = makeAssistantError("dial tcp: lookup api.example.com: no such host (ENOTFOUND)");
expect(formatAssistantErrorText(msg)).toBe(

View File

@@ -149,6 +149,15 @@ describe("sanitizeUserFacingText", () => {
).toBe("LLM request failed: connection refused by the provider endpoint.");
});
it.each(["disk full", "ENOSPC: no space left on device"])(
"rewrites disk-space failures with errorContext: %s",
(input) => {
expect(sanitizeUserFacingText(input, { errorContext: true })).toBe(
"OpenClaw could not write local session data because the disk is full. Free some disk space and try again.",
);
},
);
it("sanitizes invalid streaming event order errors", () => {
expect(
sanitizeUserFacingText(

View File

@@ -167,6 +167,24 @@ function formatTransportErrorCopy(raw: string): string | undefined {
return undefined;
}
function formatDiskSpaceErrorCopy(raw: string): string | undefined {
if (!raw) {
return undefined;
}
const lower = raw.toLowerCase();
if (
/\benospc\b/i.test(raw) ||
lower.includes("no space left on device") ||
lower.includes("disk full")
) {
return (
"OpenClaw could not write local session data because the disk is full. " +
"Free some disk space and try again."
);
}
return undefined;
}
function isReasoningConstraintErrorMessage(raw: string): boolean {
if (!raw) {
return false;
@@ -925,6 +943,11 @@ export function formatAssistantErrorText(
}
}
const diskSpaceCopy = formatDiskSpaceErrorCopy(raw);
if (diskSpaceCopy) {
return diskSpaceCopy;
}
if (isContextOverflowError(raw)) {
return (
"Context overflow: prompt too large for the model. " +
@@ -1023,6 +1046,11 @@ export function sanitizeUserFacingText(text: unknown, opts?: { errorContext?: bo
return execDeniedMessage;
}
const diskSpaceCopy = formatDiskSpaceErrorCopy(trimmed);
if (diskSpaceCopy) {
return diskSpaceCopy;
}
if (/incorrect role information|roles must alternate/i.test(trimmed)) {
return (
"Message ordering conflict - please try again. " +