nanocoai/nanoclaw · error

session_mode 'per-thread' requires honored thread ids, but t

Error message

session_mode 'per-thread' requires honored thread ids, but this wiring's thread policy resolves off ${explicit ? `(explicit threads=false)` : `(threads is NULL and channel '${key}' declares threads: false for its ${mg.is_group === 1 ? 'group' : 'dm'} context)`} — set --threads true, or keep session_mode 'shared'

What it means

plugin.json failed JSON.parse; the parser's message is chained as the cause. Syntax errors (trailing commas, comments, unquoted keys) are the usual culprits since JSON does not tolerate them.

Source

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

  // per-thread sessions structurally require honored thread ids — reject the
  // incoherent combination rather than storing it. Creation paths that resolve
  // through the declaration derive threads=1 from sessionMode 'per-thread'
  // (resolveWiringDefaults), so only explicit flags can reach this: an
  // explicit threads=false, or NULL-inherit on a context whose declared
  // `threads` is false. Undeclared (stale) adapters stay lenient on the
  // inherit arm, same as the mention checks below.
  if (w.session_mode === 'per-thread') {
    const key = mg.instance ?? mg.channel_type;
    const explicit = w.threads !== undefined && w.threads !== null;
    const honored = explicit
      ? w.threads !== 0 && w.threads !== false
      : !hasDeclaredChannelDefaults(key, mg.channel_type) ||
        (mg.is_group === 1
          ? getChannelDefaults(key, mg.channel_type).group
          : getChannelDefaults(key, mg.channel_type).dm
        ).threads;
    if (!honored) {
      throw new Error(
        `session_mode 'per-thread' requires honored thread ids, but this wiring's thread policy resolves off ` +
          (explicit
            ? `(explicit threads=false)`
            : `(threads is NULL and channel '${key}' declares threads: false for its ${mg.is_group === 1 ? 'group' : 'dm'} context)`) +
          ` — set --threads true, or keep session_mode 'shared'`,
      );
    }
  }

  if (w.engage_mode !== 'mention' && w.engage_mode !== 'mention-sticky') return;

  const channelKey = mg.instance ?? mg.channel_type;
  if (!hasDeclaredChannelDefaults(channelKey, mg.channel_type)) return;

  const decl = getChannelDefaults(channelKey, mg.channel_type);
  if (decl.mentions === 'never') {
    throw new Error(
      `engage_mode '${w.engage_mode}' can never engage on channel '${channelKey}' — its adapter declares mentions: 'never' (no mention signal is emitted; use --engage-mode pattern)`,

View on GitHub (pinned to 294ef2aee8)

Solutions

  1. Run the file through a JSON validator or 'jq . plugin.json' to locate the syntax error
  2. Remove comments and trailing commas; quote all keys and string values
  3. Resolve any leftover merge-conflict markers

Example fix

// before
{ "name": "foo", }  // trailing comma + comment
// after
{ "name": "foo" }
Defensive patterns

Strategy: validation

Validate before calling

try { JSON.parse(fs.readFileSync(manifestPath, 'utf-8')); } catch (e) {
  // surface the syntax error to the author before calling parseTemplate
}

Try / catch

try { parseTemplate(dir); } catch (e) { if (e instanceof Error && e.message.includes('is not valid JSON')) { /* re-run editor/validator, show e.cause position */ } else throw e; }

Prevention

When it happens

Trigger: plugin.json with a trailing comma, // or /* */ comments, single-quoted strings, unquoted keys, or an unterminated string — any JSON syntax error.

Common situations: Hand-editing the manifest and pasting from JS/JSON5 examples; editor auto-format inserting comments; merge conflict markers left in the file.

Related errors


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