nanocoai/nanoclaw · error

Channel '${channelKey}' declares engageMode 'pattern' withou

Error message

Channel '${channelKey}' declares engageMode 'pattern' without an engagePattern (${isGroup ? 'group' : 'dm'} context)

What it means

parseTemplate detected the legacy template layout (a context/instructions.md file exists) but no plugin.json manifest. Old-format templates cannot be parsed by the plugin-era parser; the fix is to re-fetch the template from the library where it has been republished in plugin format.

Source

Thrown at src/channels/channel-defaults.ts:98

  const session: Pick<WiringDefaults, 'session_mode' | 'threads'> = {
    session_mode: ctx.sessionMode ?? 'shared',
    // Derived, not declared: per-thread sessions structurally require honored
    // thread ids, so the stamp is a code invariant of the session mode.
    threads: ctx.sessionMode === 'per-thread' ? 1 : null,
  };

  // The effective thread policy for the wiring being created: the derived
  // stamp wins over the inherit value (mirrors validateEngageAgainstChannel).
  const effectiveThreads = session.threads === null ? ctx.threads : true;

  let mode = ctx.engageMode;
  if (mode === 'mention-sticky' && !effectiveThreads) mode = 'mention';

  if (mode !== 'pattern') return { engage_mode: mode, engage_pattern: null, ...session };

  if (!ctx.engagePattern) {
    throw new Error(
      `Channel '${channelKey}' declares engageMode 'pattern' without an engagePattern (${isGroup ? 'group' : 'dm'} context)`,
    );
  }
  return {
    engage_mode: 'pattern',
    engage_pattern: substituteName(ctx.engagePattern, agentGroupName),
    ...session,
  };
}

/** unknown_sender_policy for a messaging_groups row created in this context.
 *  `channelType` follows the same dead-named-instance discipline as
 *  resolveWiringDefaults. */
export function resolveUnknownSenderPolicy(
  channelKey: string,
  isGroup: boolean,
  channelType?: string,
): 'strict' | 'request_approval' | 'decline_notify' | 'public' {

View on GitHub (pinned to 294ef2aee8)

Solutions

  1. Re-fetch the template from the template library (it has been republished in plugin format)
  2. Alternatively hand-migrate: add a valid plugin.json manifest and reorganize per the plugin spec
  3. Delete the stale local copy so it isn't picked up again
Defensive patterns

Strategy: try-catch

Validate before calling

const isLegacy = (dir: string) =>
  fs.existsSync(path.join(dir, 'context', 'instructions.md')) &&
  !fs.existsSync(path.join(dir, 'plugin.json'));
if (isLegacy(dir)) { /* trigger re-fetch instead of parseTemplate */ }

Try / catch

try { parseTemplate(dir); } catch (e) { if (e instanceof Error && e.message.includes('predates the plugin format')) { await refetchTemplate(dir); parseTemplate(dir); } else throw e; }

Prevention

When it happens

Trigger: Passing a directory that contains context/instructions.md but lacks plugin.json — i.e. a pre-plugin-format template directory.

Common situations: Upgrading from an older version that shipped the legacy template format and reusing old cached template directories.

Related errors


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