fix: Move ensureWorkerRunning to start of hooks to prevent race condition
All hooks now call ensureWorkerRunning() BEFORE querying the database. This ensures the worker's orphaned session cleanup runs before hooks check for active sessions, preventing 404 errors when hooks try to use sessions that don't exist in worker memory after a restart. Hook order now: 1. ensureWorkerRunning() - starts worker, runs cleanup 2. Query DB - cleanup already marked orphaned sessions as failed 3. Use session - only valid sessions are processed Fixed in: - new-hook: Line 26, before DB queries - save-hook: Line 37, before DB queries - summary-hook: Line 24, before DB queries - cleanup-hook: Line 50, before DB queries This prevents the race condition where hooks would read session status before cleanup ran, then get 404 from worker after cleanup marked sessions failed.
This commit is contained in:
+16
-17
@@ -46,6 +46,12 @@ export async function cleanupHook(input?: SessionEndInput): Promise<void> {
|
||||
const { session_id, reason } = input;
|
||||
console.error('[claude-mem cleanup] Searching for active SDK session', { session_id, reason });
|
||||
|
||||
// Ensure worker is running first (runs cleanup if restarting)
|
||||
const workerReady = await ensureWorkerRunning();
|
||||
if (!workerReady) {
|
||||
console.error('[claude-mem cleanup] Worker not available - skipping HTTP cleanup');
|
||||
}
|
||||
|
||||
// Find active SDK session
|
||||
const db = new SessionStore();
|
||||
const session = db.findActiveSDKSession(session_id);
|
||||
@@ -66,30 +72,23 @@ export async function cleanupHook(input?: SessionEndInput): Promise<void> {
|
||||
});
|
||||
|
||||
// 1. Delete session via HTTP
|
||||
if (session.worker_port) {
|
||||
if (session.worker_port && workerReady) {
|
||||
try {
|
||||
// Ensure worker is running before sending cleanup request
|
||||
const workerReady = await ensureWorkerRunning();
|
||||
if (!workerReady) {
|
||||
console.error('[claude-mem cleanup] Worker not available - skipping HTTP cleanup');
|
||||
// Continue with local cleanup below
|
||||
} else {
|
||||
const response = await fetch(`http://127.0.0.1:${session.worker_port}/sessions/${session.id}`, {
|
||||
method: 'DELETE',
|
||||
signal: AbortSignal.timeout(5000)
|
||||
});
|
||||
const response = await fetch(`http://127.0.0.1:${session.worker_port}/sessions/${session.id}`, {
|
||||
method: 'DELETE',
|
||||
signal: AbortSignal.timeout(5000)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
console.error('[claude-mem cleanup] Session deleted successfully via HTTP');
|
||||
} else {
|
||||
console.error('[claude-mem cleanup] Failed to delete session:', await response.text());
|
||||
}
|
||||
if (response.ok) {
|
||||
console.error('[claude-mem cleanup] Session deleted successfully via HTTP');
|
||||
} else {
|
||||
console.error('[claude-mem cleanup] Failed to delete session:', await response.text());
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('[claude-mem cleanup] HTTP DELETE error:', error.message);
|
||||
}
|
||||
} else {
|
||||
console.error('[claude-mem cleanup] No worker port, cannot send DELETE request');
|
||||
console.error('[claude-mem cleanup] No worker available or no worker port, skipping HTTP cleanup');
|
||||
}
|
||||
|
||||
// 2. Mark session as failed in DB (if not already completed)
|
||||
|
||||
+8
-6
@@ -21,9 +21,17 @@ export async function newHook(input?: UserPromptSubmitInput): Promise<void> {
|
||||
|
||||
const { session_id, cwd, prompt } = input;
|
||||
const project = path.basename(cwd);
|
||||
|
||||
// Ensure worker is running first (runs cleanup if restarting)
|
||||
const workerReady = await ensureWorkerRunning();
|
||||
if (!workerReady) {
|
||||
throw new Error('Worker service failed to start or become healthy');
|
||||
}
|
||||
|
||||
const db = new SessionStore();
|
||||
|
||||
try {
|
||||
|
||||
// Check for any existing session (active, failed, or completed)
|
||||
let existing = db.findActiveSDKSession(session_id);
|
||||
let sessionDbId: number;
|
||||
@@ -54,12 +62,6 @@ export async function newHook(input?: UserPromptSubmitInput): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure worker service is running (v4.0.0 auto-start)
|
||||
const workerReady = await ensureWorkerRunning();
|
||||
if (!workerReady) {
|
||||
throw new Error('Worker service failed to start or become healthy');
|
||||
}
|
||||
|
||||
// Get fixed port
|
||||
const port = getWorkerPort();
|
||||
|
||||
|
||||
+6
-6
@@ -33,6 +33,12 @@ export async function saveHook(input?: PostToolUseInput): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure worker is running first (runs cleanup if restarting)
|
||||
const workerReady = await ensureWorkerRunning();
|
||||
if (!workerReady) {
|
||||
throw new Error('Worker service failed to start or become healthy');
|
||||
}
|
||||
|
||||
const db = new SessionStore();
|
||||
const session = db.findActiveSDKSession(session_id);
|
||||
|
||||
@@ -52,12 +58,6 @@ export async function saveHook(input?: PostToolUseInput): Promise<void> {
|
||||
const promptNumber = db.getPromptCounter(session.id);
|
||||
db.close();
|
||||
|
||||
// Ensure worker is running before sending observation
|
||||
const workerReady = await ensureWorkerRunning();
|
||||
if (!workerReady) {
|
||||
throw new Error('Worker service failed to start or become healthy');
|
||||
}
|
||||
|
||||
const toolStr = logger.formatTool(tool_name, tool_input);
|
||||
|
||||
logger.dataIn('HOOK', `PostToolUse: ${toolStr}`, {
|
||||
|
||||
@@ -19,6 +19,13 @@ export async function summaryHook(input?: StopInput): Promise<void> {
|
||||
}
|
||||
|
||||
const { session_id } = input;
|
||||
|
||||
// Ensure worker is running first (runs cleanup if restarting)
|
||||
const workerReady = await ensureWorkerRunning();
|
||||
if (!workerReady) {
|
||||
throw new Error('Worker service failed to start or become healthy');
|
||||
}
|
||||
|
||||
const db = new SessionStore();
|
||||
const session = db.findActiveSDKSession(session_id);
|
||||
|
||||
@@ -38,12 +45,6 @@ export async function summaryHook(input?: StopInput): Promise<void> {
|
||||
const promptNumber = db.getPromptCounter(session.id);
|
||||
db.close();
|
||||
|
||||
// Ensure worker is running before requesting summary
|
||||
const workerReady = await ensureWorkerRunning();
|
||||
if (!workerReady) {
|
||||
throw new Error('Worker service failed to start or become healthy');
|
||||
}
|
||||
|
||||
logger.dataIn('HOOK', 'Stop: Requesting summary', {
|
||||
sessionId: session.id,
|
||||
workerPort: session.worker_port,
|
||||
|
||||
Reference in New Issue
Block a user