{"record":{"id":"b3cd9960d333e539","repo":"coleam00/Archon","slug":"conversation-not-found-conversationid","errorCode":null,"errorMessage":"Conversation not found: ${conversationId}","messagePattern":"Conversation not found: (.+?)","errorType":"exception","errorClass":"ConversationNotFoundError","httpStatus":null,"severity":"error","filePath":"packages/core/src/db/conversations.ts","lineNumber":186,"sourceCode":"    values.push(updates.hidden ? 1 : 0);\n  }\n\n  if (fields.length === 0) {\n    return; // No updates\n  }\n\n  const dialect = getDialect();\n  fields.push(`updated_at = ${dialect.now()}`);\n  values.push(id);\n\n  const result = await pool.query(\n    `UPDATE remote_agent_conversations SET ${fields.join(', ')} WHERE id = $${String(i)}`,\n    values\n  );\n\n  if (result.rowCount === 0) {\n    getLog().error({ conversationId: id, fields, updates }, 'db.conversation_update_not_found');\n    throw new ConversationNotFoundError(id);\n  }\n}\n\n/**\n * Find a conversation by isolation environment ID (legacy - single result)\n * Used for provider-based lookup and shared environment detection\n */\nexport async function getConversationByIsolationEnvId(envId: string): Promise<Conversation | null> {\n  const result = await pool.query<Conversation>(\n    'SELECT * FROM remote_agent_conversations WHERE isolation_env_id = $1 LIMIT 1',\n    [envId]\n  );\n  return result.rows[0] ?? null;\n}\n\n/**\n * Find all conversations using a specific isolation environment (new UUID model)\n */","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/core/src/db/conversations.ts#L168-L204","documentation":"updateConversation builds a dynamic UPDATE on remote_agent_conversations and throws ConversationNotFoundError('Conversation not found: ${conversationId}') when the UPDATE matched zero rows. It is a typed, catchable error (name 'ConversationNotFoundError', carries conversationId) used by platform adapters to detect deleted conversations.","triggerScenarios":"Calling updateConversation with a conversation id absent from remote_agent_conversations — already soft-deleted (deleted_at set), purged, wrong id, or an id from a different database/environment.","commonSituations":"Conversation deleted in another tab/operator session while a chat still tries to persist state; stale id after a database reset; adapters (GitHub/GitLab/Gitea) resuming threads whose conversation rows were removed; typo'd or truncated id passed from a webhook payload.","solutions":["Confirm existence: SELECT id, deleted_at FROM remote_agent_conversations WHERE id='<id>';","If soft-deleted and that's unintended, clear deleted_at or create a new conversation and migrate the thread reference.","Update the caller to treat ConversationNotFoundError as 'conversation gone' — stop updating or recreate the conversation.","Check the id source (webhook, cache, config) for staleness or truncation; log the full id being used.","Verify the app is pointed at the intended database — the id may exist in another environment."],"exampleFix":"// before (from the adapters' pattern)\nawait updateConversation(id, { status: 'failed' });\n// after\ntry {\n  await updateConversation(id, { status: 'failed' });\n} catch (e) {\n  if (e instanceof ConversationNotFoundError) {\n    logger.warn({ conversationId: id }, 'conversation vanished; skipping update');\n    return;\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"const { rows } = await pool.query('SELECT 1 FROM remote_agent_conversations WHERE id = $1 AND deleted_at IS NULL', [id]);\nif (rows.length === 0) throw new ConversationNotFoundError(id); // handle before calling updateConversation","typeGuard":"function isConversationNotFound(e: unknown): e is ConversationNotFoundError {\n  return e instanceof ConversationNotFoundError;\n}","tryCatchPattern":"try {\n  await updateConversation(id, fields);\n} catch (e) {\n  if (isConversationNotFound(e)) {\n    log.warn({ conversationId: id }, 'conversation gone; skipping update');\n    return; // or recreate the conversation\n  }\n  throw e;\n}","preventionTips":["Existence-check (including deleted_at IS NULL) before bulk or batch updates.","Handle ConversationNotFoundError in adapters so deleted conversations skip updates instead of failing the run.","Avoid holding conversation ids across database resets; re-resolve from the thread mapping.","Log the id (db.conversation_update_not_found) when it happens to spot stale webhook payloads."],"tags":["database","not-found","postgresql","conversation"],"backgroundTag":"record-not-found","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}