Skip to content

Commit 01d7f0a

Browse files
committed
Fix action hang: Add hard timeout and explicit process.exit()
Root cause: Action would hang indefinitely when: - Agent request timed out but process didn't exit (event loop still active) - Gateway process or WebSocket kept Node.js alive - No hard timeout on entire action execution Changes: 1. Added 10-minute hard timeout wrapper around run() function 2. Added explicit process.exit(0/1) to force termination 3. Moved cleanup (client disconnect, stopGateway) to finally block 4. Increased agent request timeout from 5min to 8min (within 10min limit) 5. Ensured gateway process is always killed on timeout This ensures the action ALWAYS exits within 10 minutes, even if: - The LLM API returns 429 rate limit errors - The agent request hangs or times out - The WebSocket connection doesn't close properly - The gateway process hangs Fixes issue #9
1 parent 11e75cd commit 01d7f0a

File tree

264 files changed

+500406
-23
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

264 files changed

+500406
-23
lines changed

dist/index.js

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65942,12 +65942,12 @@ var require_downloadUtils = __commonJS({
6594265942
}
6594365943
exports2.downloadCacheStorageSDK = downloadCacheStorageSDK;
6594465944
var promiseWithTimeout = (timeoutMs, promise) => __awaiter2(void 0, void 0, void 0, function* () {
65945-
let timeoutHandle;
65945+
let timeoutHandle2;
6594665946
const timeoutPromise = new Promise((resolve2) => {
65947-
timeoutHandle = setTimeout(() => resolve2("timeout"), timeoutMs);
65947+
timeoutHandle2 = setTimeout(() => resolve2("timeout"), timeoutMs);
6594865948
});
6594965949
return Promise.race([promise, timeoutPromise]).then((result) => {
65950-
clearTimeout(timeoutHandle);
65950+
clearTimeout(timeoutHandle2);
6595165951
return result;
6595265952
});
6595365953
});
@@ -72304,6 +72304,26 @@ I help maintain this repository by:
7230472304

7230572305
## Personality
7230672306
I am helpful, concise, and focused on getting things done.
72307+
72308+
## Response Guidelines \u2014 CRITICAL
72309+
72310+
**NEVER show internal reasoning or thinking steps in your responses.**
72311+
72312+
When responding to issues or PRs:
72313+
- Give **clean, direct, helpful answers only**
72314+
- No "Let me check...", "I can see...", "I'll try to..." narration
72315+
- No describing what you're about to do \u2014 just do it and report results
72316+
- Format responses in clean GitHub markdown
72317+
- Be concise and to the point
72318+
- If you need to investigate, do it silently and present only findings
72319+
72320+
**Example BAD response:**
72321+
> "I can see the comment from @user! Let me check the GitHub issue to understand the context better. I can see that the web fetch returned... Let me try to check if there's a GitHub CLI available..."
72322+
72323+
**Example GOOD response:**
72324+
> "Based on the issue discussion, here's what I found: [direct answer]. The relevant code is in \`file.ts\` lines 42-58."
72325+
72326+
Remember: Users see your final response as a GitHub comment. Make it clean, professional, and valuable.
7230772327
`);
7230872328
}
7230972329
if (!fs.existsSync(memoryPath)) {
@@ -72775,15 +72795,15 @@ var OpenClawClient = class {
7277572795
setTimeout(() => {
7277672796
if (this.pendingRequests.has(id)) {
7277772797
this.pendingRequests.delete(id);
72778-
reject(new Error("Agent request timeout after 300s"));
72798+
reject(new Error("Agent request timeout after 480s (8 minutes)"));
7277972799
}
72780-
}, 3e5);
72800+
}, 48e4);
7278172801
});
7278272802
this.send(request);
7278372803
const acceptPayload = await acceptedPromise;
7278472804
core4.info(`Agent request accepted (runId=${acceptPayload?.runId ?? "unknown"}), waiting for completion...`);
7278572805
const timeoutPromise = new Promise(
72786-
(_, reject) => setTimeout(() => reject(new Error("Agent lifecycle timeout after 300s")), 3e5)
72806+
(_, reject) => setTimeout(() => reject(new Error("Agent lifecycle timeout after 480s (8 minutes)")), 48e4)
7278772807
);
7278872808
const lifecycleFallback = this.lifecycleEndPromise.then(() => {
7278972809
const streamed = this.streamBuffer.join("");
@@ -73140,8 +73160,6 @@ ${errorMsg}
7314073160
}
7314173161
throw sendError;
7314273162
}
73143-
client.disconnect();
73144-
client = null;
7314573163
if (trigger.issueNumber && !response.includes("HEARTBEAT_OK")) {
7314673164
const octokit = github2.getOctokit(githubToken);
7314773165
try {
@@ -73165,22 +73183,48 @@ _No response was generated._`;
7316573183
} else {
7316673184
core5.info("No issue/PR to post to \u2014 response logged above");
7316773185
}
73168-
await stopGateway();
7316973186
await saveWorkspace(workspacePath, repo);
7317073187
core5.info("=== OpenClaw complete ===");
7317173188
} catch (error4) {
73172-
if (client) client.disconnect();
73173-
await stopGateway().catch(() => {
73174-
});
7317573189
const errorMessage = error4 instanceof Error ? error4.message : String(error4);
7317673190
core5.error(`Error details: ${errorMessage}`);
7317773191
if (error4 instanceof Error && error4.stack) {
7317873192
core5.error(`Stack trace: ${error4.stack}`);
7317973193
}
7318073194
core5.setFailed(errorMessage);
73195+
throw error4;
73196+
} finally {
73197+
if (client) {
73198+
try {
73199+
client.disconnect();
73200+
} catch (e) {
73201+
core5.warning(`Client disconnect error: ${e}`);
73202+
}
73203+
}
73204+
try {
73205+
await stopGateway();
73206+
} catch (e) {
73207+
core5.warning(`Gateway stop error: ${e}`);
73208+
}
7318173209
}
7318273210
}
73183-
run();
73211+
var HARD_TIMEOUT_MS = 10 * 60 * 1e3;
73212+
var timeoutHandle = setTimeout(() => {
73213+
core5.error("\u23F1\uFE0F HARD TIMEOUT: Action exceeded 10 minutes, forcing exit");
73214+
stopGateway().catch(() => {
73215+
}).finally(() => {
73216+
process.exit(1);
73217+
});
73218+
}, HARD_TIMEOUT_MS);
73219+
run().then(() => {
73220+
clearTimeout(timeoutHandle);
73221+
core5.info("\u2705 Action completed successfully");
73222+
process.exit(0);
73223+
}).catch((error4) => {
73224+
clearTimeout(timeoutHandle);
73225+
core5.error(`\u274C Action failed: ${error4}`);
73226+
process.exit(1);
73227+
});
7318473228
/*! Bundled license information:
7318573229

7318673230
undici/lib/fetch/body.js:

node_modules/.bin/esbuild

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/tsc

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/tsserver

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)