nanocoai/nanoclaw · error · Error

${name} must be one of: ${allowed.join(', ')}

Error message

${name} must be one of: ${allowed.join(', ')}

What it means

Wiring creation validates enum columns (CREATE_ENUMS: ignored_message_policy drop|accumulate, session_mode shared|per-thread|agent-shared) and throws listing the allowed values when a supplied value is not in the set.

Source

Thrown at src/cli/resources/wirings.ts:216

        // Idempotent: a wiring for this pair already exists → return it
        // (defaults/validation/side-effects are skipped — nothing new is written).
        const existing = await getMessagingGroupAgentByPair(mgId, agId);
        if (existing) return existing;

        // Pass-1 parity: only defined keys enter `values` (an unset
        // engage_pattern stays absent → column NULL), enums validated.
        const values: Record<string, unknown> = {
          id: randomUUID(),
          messaging_group_id: mgId,
          agent_group_id: agId,
          created_at: new Date().toISOString(),
        };
        for (const [name, allowed] of Object.entries(CREATE_ENUMS)) {
          const v = args[name];
          if (v === undefined) continue;
          if (!allowed.includes(String(v))) {
            throw new Error(`${name} must be one of: ${allowed.join(', ')}`);
          }
          values[name] = v;
        }
        if (args.engage_pattern !== undefined) values.engage_pattern = args.engage_pattern;
        if (args.threads !== undefined) values.threads = args.threads;
        if (args.priority !== undefined) values.priority = Number(args.priority);

        // Pass-2 parity: context-aware defaults + cross-column validation.
        const mg = await requireMessagingGroup(values.messaging_group_id);
        if (values.threads !== undefined) values.threads = normalizeThreads(values.threads);

        const channelKey = mg.instance ?? mg.channel_type;
        // Undeclared (stale) channels: leave engage_mode unset so the static
        // 'mention' default applies afterwards — a trunk update alone must not
        // change ncl's creation defaults for adapters without a declaration.
        if (values.engage_mode === undefined) {
          if (hasDeclaredChannelDefaults(channelKey, mg.channel_type)) {
            const ag = await getAgentGroup(String(values.agent_group_id));

View on GitHub (pinned to 294ef2aee8)

Solutions

  1. Use an allowed value from the message: session_mode = shared|per-thread|agent-shared; ignored_message_policy = drop|accumulate
  2. Check `ncl wirings help` for current options

Example fix

# before
ncl wirings create ... --session-mode group
# after
ncl wirings create ... --session-mode agent-shared
Defensive patterns

Strategy: validation

Validate before calling

const ENUMS = { ignored_message_policy:['drop','accumulate'], session_mode:['shared','per-thread','agent-shared'] };
for (const [k, allowed] of Object.entries(ENUMS)) {
  if (v[k] !== undefined && !allowed.includes(String(v[k]))) throw new Error(`${k} must be one of: ${allowed.join(', ')}`);
}

Type guard

function isValidWiringEnum(k: string, v: string): boolean {
  const allowed: Record<string,string[]> = {
    ignored_message_policy: ['drop','accumulate'],
    session_mode: ['shared','per-thread','agent-shared'],
  };
  return !allowed[k] || allowed[k].includes(v);
}

Prevention

When it happens

Trigger: ncl wirings create --session-mode group or --ignored-message-policy skip — any value outside the declared enums.

Common situations: Guessing enum spellings ('isolate' for session_mode, 'ignore' for ignored_message_policy) or stale docs from an older version with different values.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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