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
- Re-fetch the template from the template library (it has been republished in plugin format)
- Alternatively hand-migrate: add a valid plugin.json manifest and reorganize per the plugin spec
- 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
- After upgrades, sweep template caches for legacy layouts and refresh them
- Version-stamp cached templates so stale formats are detectable
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
- ${settingsFile} hooks.SessionStart must be an array
- Unknown provider: ${name}. Registered: ${known}
- engage_mode 'pattern' requires --engage-pattern (use "." to
- engage_mode '${w.engage_mode}' can never engage on channel '
- chat-sdk bridge instance ${JSON.stringify(config.instance)}
AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28).
Data as JSON: /api/errors/51a5aed3a86727f9.
Report an issue: GitHub.