nanocoai/nanoclaw · error
agent group not found: ${agentGroupId}
Error message
agent group not found: ${agentGroupId} What it means
appendRunLog looks up the agent group in the central DB to resolve its folder under groups/. If getAgentGroup returns null the group id does not exist (never created, deleted, or typo'd), and the run log cannot be written because the target directory is unknown.
Source
Thrown at src/modules/scheduling/run-log.ts:26
* (container/agent-runner poll-loop auto-append; delivery.ts routes it here)
*/
import fs from 'fs';
import { GROUPS_DIR } from '../../config.js';
import { resolveGroupTimezone } from '../../container-config.js';
import { getAgentGroup } from '../../db/agent-groups.js';
import { formatLocalStamp } from '../../timezone.js';
export async function appendRunLog(
agentGroupId: string,
series: string,
msg: string,
): Promise<{ series: string; timestamp: string; path: string }> {
// Charset guard is the security boundary: blocks path traversal and keeps
// the id safe as a filename. Callers resolve group scope before this.
if (!/^[a-z0-9-]+$/.test(series)) throw new Error(`invalid task id: ${series}`);
const ag = await getAgentGroup(agentGroupId);
if (!ag) throw new Error(`agent group not found: ${agentGroupId}`);
const timestamp = formatLocalStamp(new Date(), await resolveGroupTimezone(agentGroupId));
const dir = `${GROUPS_DIR}/${ag.folder}/tasks`;
const file = `${dir}/${series}.md`;
fs.mkdirSync(dir, { recursive: true });
fs.appendFileSync(file, `${timestamp} — ${msg}\n`);
return { series, timestamp, path: file };
}
export async function deleteRunLog(agentGroupId: string, series: string): Promise<void> {
if (!/^[a-z0-9-]+$/.test(series)) throw new Error(`invalid task id: ${series}`);
const ag = await getAgentGroup(agentGroupId);
if (!ag) throw new Error(`agent group not found: ${agentGroupId}`);
fs.rmSync(`${GROUPS_DIR}/${ag.folder}/tasks/${series}.md`, { force: true });
}
View on GitHub (pinned to 294ef2aee8)
Solutions
- List existing groups (ncl groups list) and use the exact id.
- If the group was deleted but tasks remain, clean up or re-create the group before logging.
- Parameterize group ids from config rather than hardcoding them per environment.
Example fix
// before
await appendRunLog('my-group', series, 'ran');
// after
const groups = await listAgentGroups();
const g = groups.find(x => x.name === 'my-group');
await appendRunLog(g.id, series, 'ran'); Defensive patterns
Strategy: try-catch
Validate before calling
import { getAgentGroup } from './db/agent-groups';
const ag = await getAgentGroup(agentGroupId);
if (!ag) throw new Error(`unknown agent group: ${agentGroupId}`); Try / catch
try { await appendRunLog(groupId, series, msg); } catch (err) { if ((err as Error).message.startsWith('agent group not found')) { /* skip or re-resolve group */ } else throw err; } Prevention
- Resolve the group id once at startup and reuse it.
- Don't hardcode group ids across environments; load from config.
When it happens
Trigger: Calling appendRunLog/appendTaskLog/appendHostTaskNote with an agentGroupId that is not present in the agent_groups table — a stale id after the group was deleted, a copy-pasted id from another install, or a truncated id.
Common situations: Tasks outliving their agent group after deletion; scripts hardcoding group ids across environments; referencing a group by folder name or display name instead of its id.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- source agent group not found: ${from}
- target agent group not found: ${to}
- --status must be pending or paused
- session not found: ${sessionId}
- task series id is required
AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28).
Data as JSON: /api/errors/558163d2efcd2a8f.
Report an issue: GitHub.