nanocoai/nanoclaw · error

no verb " " on — run `ncl help

Error message

no verb "${verbArg}" on ${res.plural} — run `ncl ${res.plural} help`

What it means

An entry deeper than MAX_PLUGIN_DEPTH levels of nesting throws with its relative path. The depth cap bounds recursion on hostile or pathological trees before any content is read.

Solutions

  1. Flatten or delete the deep subtree (most often node_modules or build output — templates shouldn't ship them)
  2. Add ignore/exclusion handling in your packaging step so deep trees never enter the plugin
  3. Check MAX_PLUGIN_DEPTH in plugin-dir.ts for the exact limit
Defensive patterns

Strategy: validation

Validate before calling

let maxDepth = 0;
const walk = (d: string, depth: number) => {
  maxDepth = Math.max(maxDepth, depth);
  for (const e of fs.readdirSync(d, { withFileTypes: true })) {
    if (e.isDirectory()) walk(path.join(d, e.name), depth + 1);
  }
};
walk(dir, 0);
if (maxDepth > MAX_PLUGIN_DEPTH) { /* prune deep subtrees before walkPluginDir */ }

Try / catch

try { walkPluginDir(dir); } catch (e) { if (e instanceof Error && e.message.includes('nesting depth')) { /* delete node_modules/build output and retry */ } else throw e; }

Prevention

When it happens

Trigger: A plugin whose directory tree nests deeper than the MAX_PLUGIN_DEPTH constant — e.g. deeply nested node_modules chains or generated build artifacts.

Common situations: Committing node_modules or build output into a template; machine-generated trees with pathological nesting; chained extracted archives.

Related errors


AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28). Data as JSON: /api/errors/af6baa7eaf690ef5. Report an issue: GitHub.

Appendix: source

Thrown at src/cli/commands/help.ts:96

        description: `Show ${res.name} resource details; \`${res.plural} help <verb>\` for one verb in depth.`,
        access: 'open',
        resource: res.plural,
        parseArgs: (raw) => raw,
        handler: async (args, ctx) => {
          const cliScope = await getCliScope(ctx);
          const lines: string[] = [];

          // `ncl <resource> help <verb>` arrives via the dispatcher's
          // longest-prefix fallback (`groups-help-create` → `groups-help` +
          // id=`create`) and renders that verb's deep help — full description,
          // flags, examples. No new routing. Group-scope auto-fill also puts
          // the caller's agent group ID into `id` on groups/destinations —
          // that's not a verb request, so ignore it.
          const autoFilled = ctx.caller === 'agent' && args.id === ctx.agentGroupId;
          const verbArg = !autoFilled && typeof args.id === 'string' ? args.id : null;
          if (verbArg) {
            const deep = renderVerbHelp(res, verbArg);
            if (!deep) throw new Error(`no verb "${verbArg}" on ${res.plural} — run \`ncl ${res.plural} help\``);
            return deep;
          }

          lines.push(`${res.plural}: ${res.description}`);

          if (cliScope === 'group' && GROUP_SCOPE_RESOURCES.has(res.plural)) {
            lines.push('');
            lines.push('Note: --id and group args are auto-filled to your agent group. You do not need to pass them.');
          }

          lines.push('');

          // Verbs — one summary line each; deep help is a verb away. Only the
          // exceptional access levels are tagged: `open` is the unmarked default.
          const idAutoFilled = cliScope === 'group' && (res.plural === 'groups' || res.plural === 'destinations');
          const idHint = idAutoFilled ? '' : ' <id>';
          const tag = (access: string | undefined) => (!access || access === 'open' ? '' : ` [${access}]`);
          const verbs: string[] = [];

View on GitHub (pinned to 294ef2aee8)