{"record":{"id":"8821923ba97e25cc","repo":"nanocoai/nanoclaw","slug":"group-folder-groups-folder-already-exists-on","errorCode":null,"errorMessage":"group folder 'groups/${folder}' already exists on disk but no agent group claims it — deleting a group never removes its folder, and creating a new group over it would silently adopt the old group's data under a new identity. Move or remove the folder, or pick a different --folder.","messagePattern":"group folder 'groups/(.+?)' already exists on disk but no agent group claims it — deleting a group never removes its folder, and creating a new group over it would silently adopt the old group's data under a new identity\\. Move or remove the folder, or pick a different --folder\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/resources/groups.ts","lineNumber":175,"sourceCode":"        }\n        const folder = args.folder as string;\n        if (!folder) throw new Error('--folder is required');\n        // The template path validates through createAgentFromTemplate; the bare\n        // path used to validate nowhere, minting folders the runtime label\n        // grammar refuses at every spawn.\n        assertValidGroupFolder(folder);\n        const name = (args.name as string) ?? folder;\n        const existing = await getAgentGroupByFolder(folder);\n        if (existing) {\n          await initGroupFilesystem(existing); // ensure a reused group is fully configured too (idempotent; also repairs a missing workspace folder)\n          return existing;\n        }\n        // Fresh-create branch only: a folder on disk with no claiming DB row\n        // is deleted-group residue (delete never removes groups/<folder>/) or\n        // an operator-placed dir — minting a new id over it would silently\n        // re-scope the old group's data under a new identity.\n        if (groupFolderExistsOnDisk(folder)) {\n          throw new Error(\n            `group folder 'groups/${folder}' already exists on disk but no agent group claims it — ` +\n              `deleting a group never removes its folder, and creating a new group over it would silently ` +\n              `adopt the old group's data under a new identity. Move or remove the folder, or pick a different --folder.`,\n          );\n        }\n        const id = `ag-${randomUUID()}`;\n        const group: AgentGroup = { id, name, folder, agent_provider: null, created_at: new Date().toISOString() };\n        await createAgentGroup(group);\n        // Provision the workspace folder and the `container_configs` row that\n        // `getContainerConfig` and the spawn path require. Without this, a\n        // group created via `ncl groups create` would throw \"Container config\n        // not found\" on first spawn and stay broken until the host restart\n        // backfill ran (#2415). The template branch above provisions its own\n        // config + folder in `createAgentFromTemplate`; this covers the bare\n        // path. Mirrors what `setup/register.ts` does after creating an agent\n        // group via the setup flow. The config row is stamped with the\n        // instance default provider (`ensureContainerConfig` inside) — per-group\n        // `groups config update --provider` still wins.","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/cli/resources/groups.ts#L157-L193","documentation":"`ncl groups create` refuses to create a group whose folder already exists on disk (groups/<folder>/) when no agent_groups row claims that folder. Deleting a group never removes its folder, so a directory without a DB row is residue from a deleted group; creating over it would silently adopt the old group's data (workspace, memory, skills) under a brand-new group id.","triggerScenarios":"`ncl groups create --folder X` where groups/X/ exists on disk but the agent_groups table has no row with folder=X. Typical after `ncl groups delete --id <id>` was run earlier and groups/X/ was left behind.","commonSituations":"Delete-then-recreate workflows (delete a group, immediately try to recreate one with the same folder name); operator manually placed a directory under groups/; restoring from a partial backup where the filesystem has folders the DB doesn't know about.","solutions":["Remove or move the leftover folder: `rm -rf groups/X` (after confirming you don't need its data/memory), then retry the create","Pick a different folder: `ncl groups create --folder X-new`","If you actually want the old data, restore the deleted DB row instead of recreating the group"],"exampleFix":"# before\nncl groups delete --id ag-...\nncl groups create --folder my-agent   # throws\n# after\nmv groups/my-agent groups/my-agent.bak\nncl groups create --folder my-agent","handlingStrategy":"validation","validationCode":"import { existsSync } from 'node:fs';\nconst dir = `groups/${folder}`;\nif (existsSync(dir)) {\n  const claimed = await getAgentGroupByFolder(folder);\n  if (!claimed) { /* residue: move/remove before create */ }\n}","typeGuard":"const isClaimedFolder = (r: unknown): r is { id: string } => !!r && typeof (r as any).id === 'string';","tryCatchPattern":"try { await createGroup(folder) } catch (e) { if (e instanceof Error && e.message.includes('already exists on disk')) { await renameOld(folder); await createGroup(folder); } else throw e; }","preventionTips":["After `ncl groups delete`, also remove groups/<folder>/ and data/v2-sessions/<group-id>/ if you intend to reuse the folder name","Pre-check folder availability before scripting creates"],"tags":["cli","groups","filesystem","state-mismatch"],"backgroundTag":"orphaned-resource-conflict","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}