{"record":{"id":"2520d40b41df10a5","repo":"nanocoai/nanoclaw","slug":"group-not-found-id","errorCode":null,"errorMessage":"group not found: ${id}","messagePattern":"group not found: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/resources/groups.ts","lineNumber":220,"sourceCode":"          ? formatRestampResult(data as RestampResult)\n          : JSON.stringify(localizeIsoTimestamps(data), null, 2),\n    },\n    delete: {\n      access: 'approval',\n      description:\n        'Delete an agent group and its dependent rows (sessions, destinations, approvals, role grants, ' +\n        'memberships, channel wirings). FK-ordered cascade in a single transaction. ' +\n        'Use --id <group-id>. Out of scope: killing running containers, on-disk cleanup of groups/<folder>/ and data/v2-sessions/<group-id>/. ' +\n        'The leftover groups/<folder>/ blocks re-creating a group under the same folder name until it is moved or removed.',\n      handler: async (args) => {\n        const id = args.id as string;\n        if (!id) throw new Error('--id is required');\n        const db = getDb();\n\n        // Verify the group exists before doing anything — preserves the\n        // genericDelete behaviour of throwing \"not found\" for unknown IDs.\n        const exists = await db.get('SELECT 1 FROM agent_groups WHERE id = ? LIMIT 1', id);\n        if (!exists) throw new Error(`group not found: ${id}`);\n\n        const hasAgentDestinations = await hasTable(db, 'agent_destinations');\n        const hasPendingApprovals = await hasTable(db, 'pending_approvals');\n\n        // FK-ordered cascade. The async driver transaction rolls\n        // back the whole thing if any statement throws (e.g. an FK constraint\n        // we missed), so the central DB stays consistent. The `removed` counts\n        // are sourced from each DELETE's `changes` so they describe exactly\n        // what the transaction did, not a separate pre-flight snapshot.\n        const removed = await db.transaction(async () => {\n          const counts = {\n            sessions: 0,\n            pending_questions: 0,\n            pending_approvals: 0,\n            agent_destinations_owned: 0,\n            agent_destinations_pointing: 0,\n            pending_sender_approvals: 0,\n            pending_channel_approvals: 0,","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/cli/resources/groups.ts#L202-L238","documentation":"`ncl groups delete --id <id>` was given an id that matches no row in the agent_groups table. The handler explicitly checks existence first (preserving genericDelete's not-found semantics) before attempting the FK-ordered cascade delete in a transaction.","triggerScenarios":"Passing a stale, mistyped, or already-deleted group id, e.g. `ncl groups delete --id ag-typo` or re-running a delete script after the group was already removed.","commonSituations":"Re-running an idempotency-intended delete script (delete is not idempotent — it throws instead of no-op'ing); copy/paste truncating the uuid; referencing a group deleted by another operator or by a migration; using the folder name instead of the ag-<uuid> id.","solutions":["Check what exists: `ncl groups list` — if the group is gone, the delete already succeeded; treat this as done","If you meant a different group, copy the exact id (ag-<uuid>) from the list output and retry","In scripts, guard with a existence check or catch the not-found message and exit 0"],"exampleFix":"# before\nncl groups delete --id ag-old-id\n# after\nncl groups list   # confirm current ids\nncl groups delete --id ag-actual-uuid\n# script-safe:\nncl groups delete --id \"$ID\" 2>/dev/null || true","handlingStrategy":"try-catch","validationCode":"const row = await db.get('SELECT 1 FROM agent_groups WHERE id = ?', id);\nif (!row) { /* already gone — treat as success */ }","typeGuard":"const exists = (r: unknown): r is { '1': number } => !!r;","tryCatchPattern":"try { await deleteGroup(id) } catch (e) { if (e instanceof Error && e.message.startsWith('group not found:')) return; /* idempotent no-op */ throw e; }","preventionTips":["Make delete scripts idempotent by swallowing not-found","Verify ids via `ncl groups list` right before deleting"],"tags":["cli","not-found","groups","delete"],"backgroundTag":"entity-not-found","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}