nanocoai/nanoclaw · error · Error

${msg} ${usage}

Error message

${msg}

${usage}

What it means

This is not a distinct failure but the wrapper around strict validation for custom verbs: any validateArgs failure (unknown flag, required, type, JSON, enum) is re-thrown with the verb's rendered usage block appended, separated by a blank line, and the original error preserved as `cause`. It exists so a human or agent caller can fix the invocation in one round-trip instead of issuing a separate help command.

Source

Thrown at src/cli/crud.ts:544

  // can fix the invocation without a second help round-trip.
  if (def.customOperations) {
    for (const [verb, op] of Object.entries(def.customOperations)) {
      const declared = op.args;
      register({
        name: `${def.plural}-${verb.replace(/ /g, '-')}`,
        action: `${def.plural}.${verb.replace(/ /g, '.')}`,
        description: op.description,
        access: op.access,
        hostOnly: op.hostOnly,
        resource: def.plural,
        parseArgs: declared
          ? (raw) => {
              try {
                return validateArgs(declared, normalizeArgs(raw));
              } catch (e) {
                const usage = renderVerbHelp(def, verb);
                const msg = e instanceof Error ? e.message : String(e);
                throw new Error(usage ? `${msg}\n\n${usage}` : msg, { cause: e });
              }
            }
          : (raw) => normalizeArgs(raw),
        handler: async (args, ctx) => op.handler(args as Record<string, unknown>, ctx),
        formatHuman: op.formatHuman,
      });
    }
  }
}

View on GitHub (pinned to 294ef2aee8)

Solutions

  1. Read the full message — the usage block after the blank line lists accepted flags and required markers
  2. Fix the underlying validation issue named on the first line
  3. Programmatically, inspect `error.cause` for the original un-wrapped validation error
  4. For tests, assert with `.startsWith()` or match on the first line only

Example fix

// before (test asserts exact message)
expect(() => parse(args)).toThrow('unknown flag --rebild');

// after
expect(() => parse(args)).toThrow(/^unknown flag --rebild/);
Defensive patterns

Strategy: try-catch

Validate before calling

null

Try / catch

catch (e) {
  if (e instanceof Error) {
    const [first, usage] = e.message.split('\n\n');
    // first line = underlying validation error; usage = rendered help
    const root = e.cause; // original validateArgs error
  }
}

Prevention

When it happens

Trigger: Any of errors 32–38 occurring on a custom operation (a verb defined in `customOperations` with declared `args`). E.g. `ncl groups restart --id g1 --mesage x` produces "unknown flag --mesage" plus the restart usage text.

Common situations: Parsers that split the error on newlines and only surface the first line lose the usage context; agents that retry blindly instead of reading the appended usage; tests asserting exact error strings breaking when usage is appended.

Related errors


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