From f218650b8566ea46895c9292e0e975a2edab9368 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 2 Jun 2026 11:43:22 +0000 Subject: [PATCH] fix(scheduler): mark orphaned live assets error immediately when recorder stops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a capture sidecar crashes before finalize() runs (e.g. wrong node, filter error, hardware fault), the asset stays 'live' indefinitely — library shows 'Recording' badge for up to 120 minutes until the stale-timeout fires. Add an orphan check that runs every scheduler tick: if an asset is 'live' and its recorder is 'stopped', mark it 'error' immediately. This runs before the 120-minute staleness guard so the library clears within 15 seconds. 🤖 Generated with Claude Code --- services/mam-api/src/scheduler.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/services/mam-api/src/scheduler.js b/services/mam-api/src/scheduler.js index a929bc1..6787dd9 100644 --- a/services/mam-api/src/scheduler.js +++ b/services/mam-api/src/scheduler.js @@ -135,6 +135,24 @@ async function tick() { } } + // Orphaned live assets: recorder stopped but asset still 'live'. + // Happens when the capture sidecar crashes before finalize() runs. + // Mark error immediately so the library doesn't show "Recording" forever. + const orphanResult = await client.query( + `UPDATE assets a + SET status = 'error', updated_at = NOW() + FROM recorders r + WHERE a.status = 'live' + AND a.display_name = r.current_session_id + AND r.status = 'stopped' + RETURNING a.id, a.display_name` + ); + if (orphanResult.rows.length > 0) { + for (const row of orphanResult.rows) { + console.warn(`[scheduler] orphaned live asset (recorder stopped): ${row.id} (${row.display_name})`); + } + } + const LIVE_TIMEOUT_MINUTES = parseInt(process.env.LIVE_ASSET_TIMEOUT_MINUTES || '120', 10); const staleResult = await client.query( `UPDATE assets