{"record":{"id":"c5a96c27bcd12d33","repo":"nanocoai/nanoclaw","slug":"session-mode-per-thread-requires-honored-thread","errorCode":null,"errorMessage":"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'","messagePattern":"session_mode 'per-thread' requires honored thread ids, but this wiring's thread policy resolves off (.+?)' declares threads: false for its (.+?) context\\)`\\} — set --threads true, or keep session_mode 'shared'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/channels/channel-defaults.ts","lineNumber":190,"sourceCode":"  // per-thread sessions structurally require honored thread ids — reject the\n  // incoherent combination rather than storing it. Creation paths that resolve\n  // through the declaration derive threads=1 from sessionMode 'per-thread'\n  // (resolveWiringDefaults), so only explicit flags can reach this: an\n  // explicit threads=false, or NULL-inherit on a context whose declared\n  // `threads` is false. Undeclared (stale) adapters stay lenient on the\n  // inherit arm, same as the mention checks below.\n  if (w.session_mode === 'per-thread') {\n    const key = mg.instance ?? mg.channel_type;\n    const explicit = w.threads !== undefined && w.threads !== null;\n    const honored = explicit\n      ? w.threads !== 0 && w.threads !== false\n      : !hasDeclaredChannelDefaults(key, mg.channel_type) ||\n        (mg.is_group === 1\n          ? getChannelDefaults(key, mg.channel_type).group\n          : getChannelDefaults(key, mg.channel_type).dm\n        ).threads;\n    if (!honored) {\n      throw new Error(\n        `session_mode 'per-thread' requires honored thread ids, but this wiring's thread policy resolves off ` +\n          (explicit\n            ? `(explicit threads=false)`\n            : `(threads is NULL and channel '${key}' declares threads: false for its ${mg.is_group === 1 ? 'group' : 'dm'} context)`) +\n          ` — set --threads true, or keep session_mode 'shared'`,\n      );\n    }\n  }\n\n  if (w.engage_mode !== 'mention' && w.engage_mode !== 'mention-sticky') return;\n\n  const channelKey = mg.instance ?? mg.channel_type;\n  if (!hasDeclaredChannelDefaults(channelKey, mg.channel_type)) return;\n\n  const decl = getChannelDefaults(channelKey, mg.channel_type);\n  if (decl.mentions === 'never') {\n    throw new Error(\n      `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)`,","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/channels/channel-defaults.ts#L172-L208","documentation":"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.","triggerScenarios":"plugin.json with a trailing comma, // or /* */ comments, single-quoted strings, unquoted keys, or an unterminated string — any JSON syntax error.","commonSituations":"Hand-editing the manifest and pasting from JS/JSON5 examples; editor auto-format inserting comments; merge conflict markers left in the file.","solutions":["Run the file through a JSON validator or 'jq . plugin.json' to locate the syntax error","Remove comments and trailing commas; quote all keys and string values","Resolve any leftover merge-conflict markers"],"exampleFix":"// before\n{ \"name\": \"foo\", }  // trailing comma + comment\n// after\n{ \"name\": \"foo\" }","handlingStrategy":"validation","validationCode":"try { JSON.parse(fs.readFileSync(manifestPath, 'utf-8')); } catch (e) {\n  // surface the syntax error to the author before calling parseTemplate\n}","typeGuard":null,"tryCatchPattern":"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; }","preventionTips":["Lint plugin.json with jq or a JSON schema validator in CI","Use strict-JSON editors that reject comments and trailing commas"],"tags":["json","manifest","parse-error"],"backgroundTag":"invalid-json","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}