openclaw - 💡(How to fix) Fix fix(channels): most channels missing namedAccountPromotionKeys — multi-account promotion strips shared defaults [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#62387Fetched 2026-04-08 03:05:04
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Participants
Timeline (top)
cross-referenced ×1

When a channel config has named accounts but no default, the doctor/channels add migration moves top-level account-scoped keys into accounts.default. For channels that declare namedAccountPromotionKeys, this is limited to credentials only — policy keys stay at the channel top level as shared defaults.

Problem: Most channels do NOT declare namedAccountPromotionKeys. The guard in resolveSingleAccountKeysToMove() short-circuits when the value is undefined:

if (
  hasNamedAccounts &&
  namedAccountPromotionKeys &&  // undefined for most channels → guard skipped
  !namedAccountPromotionKeys.includes(key)
) {
  return false;
}

This means ALL movable keys (groupPolicy, allowFrom, defaultTo, credentials, etc.) get swept into accounts.default, stripping shared defaults from existing named accounts.

Root Cause

When a channel config has named accounts but no default, the doctor/channels add migration moves top-level account-scoped keys into accounts.default. For channels that declare namedAccountPromotionKeys, this is limited to credentials only — policy keys stay at the channel top level as shared defaults.

Problem: Most channels do NOT declare namedAccountPromotionKeys. The guard in resolveSingleAccountKeysToMove() short-circuits when the value is undefined:

if (
  hasNamedAccounts &&
  namedAccountPromotionKeys &&  // undefined for most channels → guard skipped
  !namedAccountPromotionKeys.includes(key)
) {
  return false;
}

This means ALL movable keys (groupPolicy, allowFrom, defaultTo, credentials, etc.) get swept into accounts.default, stripping shared defaults from existing named accounts.

Fix Action

Fix / Workaround

ChannelSourceStatus
TelegramBUNDLED_NAMED_ACCOUNT_PROMOTION_FALLBACKS✅ Patched
MatrixPlugin setup surface (namedAccountPromotionKeys)✅ Patched

Both work and personal inherit groupPolicy and allowFrom via the { ...base, ...account } merge in mergeSlackAccountConfig().

Code Example

if (
  hasNamedAccounts &&
  namedAccountPromotionKeys &&  // undefined for most channels → guard skipped
  !namedAccountPromotionKeys.includes(key)
) {
  return false;
}

---

channels:
  slack:
    groupPolicy: "open"
    allowFrom: ["U123"]
    accounts:
      work:
        botToken: "xoxb-work"
      personal:
        botToken: "xoxb-personal"

---

channels:
  slack:
    accounts:
      default:
        groupPolicy: "open"
        allowFrom: ["U123"]
      work:
        botToken: "xoxb-work"     # lost groupPolicy + allowFrom
      personal:
        botToken: "xoxb-personal" # lost groupPolicy + allowFrom
RAW_BUFFERClick to expand / collapse

Summary

When a channel config has named accounts but no default, the doctor/channels add migration moves top-level account-scoped keys into accounts.default. For channels that declare namedAccountPromotionKeys, this is limited to credentials only — policy keys stay at the channel top level as shared defaults.

Problem: Most channels do NOT declare namedAccountPromotionKeys. The guard in resolveSingleAccountKeysToMove() short-circuits when the value is undefined:

if (
  hasNamedAccounts &&
  namedAccountPromotionKeys &&  // undefined for most channels → guard skipped
  !namedAccountPromotionKeys.includes(key)
) {
  return false;
}

This means ALL movable keys (groupPolicy, allowFrom, defaultTo, credentials, etc.) get swept into accounts.default, stripping shared defaults from existing named accounts.

Channels with the guard

ChannelSourceStatus
TelegramBUNDLED_NAMED_ACCOUNT_PROMOTION_FALLBACKS✅ Patched
MatrixPlugin setup surface (namedAccountPromotionKeys)✅ Patched

Channels WITHOUT the guard (affected)

Slack, Discord, WhatsApp, Signal, IRC, MS Teams, Google Chat, iMessage, Line, Nostr, Mattermost, Synology Chat, BlueBubbles, Tlon, Twitch, Zalo, and all other extensions.

Repro

Before migration — top-level acts as inherited base for all accounts:

channels:
  slack:
    groupPolicy: "open"
    allowFrom: ["U123"]
    accounts:
      work:
        botToken: "xoxb-work"
      personal:
        botToken: "xoxb-personal"

Both work and personal inherit groupPolicy and allowFrom via the { ...base, ...account } merge in mergeSlackAccountConfig().

After doctor --fix — shared defaults moved into phantom default:

channels:
  slack:
    accounts:
      default:
        groupPolicy: "open"
        allowFrom: ["U123"]
      work:
        botToken: "xoxb-work"     # lost groupPolicy + allowFrom
      personal:
        botToken: "xoxb-personal" # lost groupPolicy + allowFrom

work and personal no longer inherit those values.

Expected behavior

Each channel should declare namedAccountPromotionKeys listing only credential/auth keys that are per-account by nature (e.g., botToken, appToken, signingSecret). Policy keys (groupPolicy, allowFrom, defaultTo) should remain at the channel top level as shared defaults.

Alternatively, the fallback when namedAccountPromotionKeys is undefined could default to credentials-only (conservative) rather than move-everything (destructive).

Relevant code

  • src/channels/plugins/setup-helpers.tsresolveSingleAccountKeysToMove()
  • src/commands/doctor/shared/channel-legacy-config-migrate.ts (calls via seedMissingDefaultAccountsFromSingleAccountBase)
  • BUNDLED_NAMED_ACCOUNT_PROMOTION_FALLBACKS — only has Telegram
  • BUNDLED_SINGLE_ACCOUNT_PROMOTION_FALLBACKS — only has Telegram

Related PRs

  • #27334 — Original migration
  • #60630 — Telegram setup promotion surface (open)
  • #62263 — Telegram doctor migration fix (merged)
  • #61437 — Matrix avatar setup into account config (merged)

extent analysis

TL;DR

The issue can be fixed by adding a default value for namedAccountPromotionKeys to only include credential keys when the value is undefined, preventing all movable keys from being swept into accounts.default.

Guidance

  • Review the resolveSingleAccountKeysToMove() function to understand how it handles namedAccountPromotionKeys and how it can be modified to default to credentials-only when the value is undefined.
  • Consider adding a default value for namedAccountPromotionKeys in channels that do not declare it, to prevent shared defaults from being stripped.
  • Update the seedMissingDefaultAccountsFromSingleAccountBase function to handle the case where namedAccountPromotionKeys is undefined and default to credentials-only.
  • Test the changes with different channel configurations to ensure that shared defaults are preserved and only credential keys are moved into accounts.default.

Example

if (
  hasNamedAccounts &&
  (!namedAccountPromotionKeys || !namedAccountPromotionKeys.includes(key))
) {
  // default to credentials-only when namedAccountPromotionKeys is undefined
  const credentialKeys = ['botToken', 'appToken', 'signingSecret'];
  if (credentialKeys.includes(key)) {
    return true;
  }
  return false;
}

Notes

The provided code snippet is a possible solution, but it may need to be adapted to the specific requirements of the project. Additionally, the changes should be thoroughly tested to ensure that they do not introduce any regressions.

Recommendation

Apply a workaround by adding a default value for namedAccountPromotionKeys to only include credential keys when the value is undefined, as shown in the example code snippet. This will prevent all movable keys from being swept into accounts.default and preserve shared defaults.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Each channel should declare namedAccountPromotionKeys listing only credential/auth keys that are per-account by nature (e.g., botToken, appToken, signingSecret). Policy keys (groupPolicy, allowFrom, defaultTo) should remain at the channel top level as shared defaults.

Alternatively, the fallback when namedAccountPromotionKeys is undefined could default to credentials-only (conservative) rather than move-everything (destructive).

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING