{"record":{"id":"c7f9537fdf6372f0","repo":"affaan-m/ECC","slug":"session-not-found-session-id","errorCode":null,"errorMessage":"Session not found: {session_id}","messagePattern":"Session not found: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"ecc2/src/session/store.rs","lineNumber":1305,"sourceCode":"        pid: Option<u32>,\n    ) -> Result<()> {\n        let updated = self.conn.execute(\n            \"UPDATE sessions\n             SET state = ?1,\n                 pid = ?2,\n                 updated_at = ?3,\n                 last_heartbeat_at = ?3\n             WHERE id = ?4\",\n            rusqlite::params![\n                state.to_string(),\n                pid.map(i64::from),\n                chrono::Utc::now().to_rfc3339(),\n                session_id,\n            ],\n        )?;\n\n        if updated == 0 {\n            anyhow::bail!(\"Session not found: {session_id}\");\n        }\n\n        self.refresh_session_board_meta()?;\n        Ok(())\n    }\n\n    pub fn update_state(&self, session_id: &str, state: &SessionState) -> Result<()> {\n        let current_state = self\n            .conn\n            .query_row(\n                \"SELECT state FROM sessions WHERE id = ?1\",\n                [session_id],\n                |row| row.get::<_, String>(0),\n            )\n            .optional()?\n            .map(|raw| SessionState::from_db_value(&raw))\n            .ok_or_else(|| anyhow::anyhow!(\"Session not found: {session_id}\"))?;\n","sourceCodeStart":1287,"sourceCodeEnd":1323,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/ecc2/src/session/store.rs#L1287-L1323","documentation":"SessionStore::update_state_and_pid updates a session's state and OS pid in the SQLite sessions table. After executing the UPDATE it checks the affected row count; zero rows means no session with that id exists, so it bails with 'Session not found: {session_id}'. The store refuses to silently no-op writes to nonexistent sessions so callers never operate on stale data.","triggerScenarios":"Calling update_state_and_pid(session_id, ...) with an id that is not in the sessions table — typically a session that was already deleted, a typo'd id, or a session removed by another process (e.g. stop_session_recorded, delete) between listing and updating.","commonSituations":"A reaper/enforcer loop (enforce_session_heartbeats_with) resumed crashed sessions using ids read earlier while another worker concurrently deleted them; resume flows targeting a session id from a stale CLI arg or config; racing resume_session_with_program against cleanup.","solutions":["Verify the session id exists (query the sessions table or a get/list API) before calling update_state_and_pid.","Re-fetch the current session list if the id came from cached or earlier-read state; the session was likely deleted concurrently.","Check the id string for typos, trimming, or wrong-source ids (e.g. external task id vs session id).","If 'not found' is an expected outcome in your flow, catch the error and treat it as a no-op rather than retrying."],"exampleFix":"// before\nstore.update_state_and_pid(&session_id, state, pid)?; // bails if deleted\n\n// after\nif store.get_session(&session_id)?.is_some() {\n    store.update_state_and_pid(&session_id, state, pid)?;\n} else {\n    tracing::warn!(session_id, \"session already gone; skipping state/pid update\");\n}","handlingStrategy":"validation","validationCode":"fn session_exists(store: &SessionStore, id: &str) -> anyhow::Result<bool> {\n    Ok(store.get_session(id)?.is_some())\n}\n// call site: if !session_exists(&store, &session_id)? { skip/log; return }","typeGuard":"fn valid_session_id(id: &str) -> bool {\n    !id.trim().is_empty() && id.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')\n}","tryCatchPattern":"match store.update_state_and_pid(id, state, pid) {\n    Err(e) if e.to_string().contains(\"Session not found\") => warn_and_skip(id),\n    other => other?,\n}","preventionTips":["Always resolve ids from a fresh store query, never from cached snapshots.","Check existence before mutating in enforcer/resume loops.","Treat not-found during cleanup as a benign race and skip, not retry."],"tags":["sqlite","session-store","record-not-found","concurrency"],"backgroundTag":"record-not-found","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}