hermes - ✅(Solved) Fix `hermes profile create --clone` copies exclusive platform credentials causing multi-profile gateway conflicts [3 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

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
NousResearch/hermes-agent#17080Fetched 2026-04-29 06:37:20
View on GitHub
Comments
0
Participants
1
Timeline
10
Reactions
0
Author
Participants
Timeline (top)
labeled ×5cross-referenced ×3closed ×1renamed ×1

Error Message

API Error 500: Gateway health check timed out after 15000ms In hermes-web-ui, this surfaces as a "profile loading error" that is difficult to diagnose. When multiple profiles hold the same token and start simultaneously, hermes-agent's platform adapter fails during initialization or scoped lock acquisition (the scoped lock mechanism is described in #4587). The error is swallowed by the gateway process; the outer layer only sees a health check timeout.

Root Cause

--clone currently performs a full copy: it copies the source profile's .env and config.yaml verbatim into the new profile directory. This means:

  • WEIXIN_TOKEN / WEIXIN_ACCOUNT_ID / TELEGRAM_BOT_TOKEN / DISCORD_BOT_TOKEN / SLACK_APP_TOKEN / SIGNAL_PHONE_NUMBER and other exclusive platform credentials are copied as-is
  • config.yaml entries like platforms.<name>.enabled: true are preserved
  • Embedded credentials in config.yaml (e.g., platforms.weixin.token, platforms.weixin.extra.account_id) are also copied

Exclusive platform credentials are fundamentally "one-to-one identity bindings":

  • A Weixin token maps to exactly one bot instance
  • A Telegram bot token can only getUpdates from one process at a time
  • Discord/Slack long-lived connections are mutually exclusive
  • Signal, WhatsApp Business, etc. follow the same pattern

When multiple profiles hold the same token and start simultaneously, hermes-agent's platform adapter fails during initialization or scoped lock acquisition (the scoped lock mechanism is described in #4587). The error is swallowed by the gateway process; the outer layer only sees a health check timeout.

Fix Action

Fix / Workaround

Current web-ui Workaround

However, this is an upper-layer patch — the CLI's default behavior is unchanged, so command-line users and other integrations (e.g., custom scripts) will still hit this issue. A fix at the hermes-agent level would be ideal.

PR fix notes

PR #283: 修复: Profile clone 时智能清理独占平台凭据 + 平台设置独占警告

Description (problem / solution / changelog)

问题 / Problem

hermes profile create <name> --clone 完整复制源 profile 的 .env + config.yaml(含独占型平台凭据如 WEIXIN_TOKEN / TELEGRAM_BOT_TOKEN),导致多个 profile 共享同一身份 token。hermes-agent 在 platform adapter 初始化或 scoped lock 获取阶段失败,gateway 健康检查持续 15s 超时:

hermes profile create <name> --clone copies the source profile's .env + config.yaml verbatim (including exclusive platform credentials like WEIXIN_TOKEN / TELEGRAM_BOT_TOKEN), causing multiple profiles to share the same identity token. hermes-agent fails during platform adapter initialization or scoped lock acquisition, and the gateway health check times out after 15s:

API Error 500: Gateway health check timed out after 15000ms

根因 / Root Cause

hermes-agent 的 acquire_scoped_lock() 实现了 token 级别的互斥锁(gateway/status.py),防止同一 bot token 被多个 gateway 实例同时使用。--clone 完整复制凭据后,新旧 profile 持有完全相同的 token → 后启动的 gateway 必然 lock 失败。

hermes-agent's acquire_scoped_lock() implements token-level mutual exclusion (gateway/status.py), preventing the same bot token from being used by multiple gateway instances simultaneously. After --clone copies credentials verbatim, both profiles hold the exact same token → the later gateway inevitably fails the lock.

受影响的 7 个平台 / Affected platforms (all call _acquire_platform_lock in gateway/platforms/*.py): telegram, discord, slack, whatsapp, signal, weixin, feishu

修复方案 / Fix

1. 后端:智能克隆清理 / Backend: Smart Clone Cleanup (profile-credentials.ts)

clone 完成后自动 / Automatically after clone:

  1. <profile>/.env 删除匹配独占平台的环境变量(写 .env.bak.* 备份)/ Strip exclusive platform env vars from .env (backup to .env.bak.*)
  2. <profile>/config.yaml 中把 platforms.<exclusive>.enabled 置为 false / Set platforms.<exclusive>.enabled to false in config.yaml
  3. 清理节点直挂 + extra 子节点下的敏感字段(token / app_secret / account_id 等)/ Strip sensitive credential fields from platform nodes and their extra sub-nodes

2. 前端 toast 通知 / Frontend Toast Notification

clone 完成后 toast 显示清理摘要 / Toast displays cleanup summary after clone:

  • 被剥离的 .env 变量 / Stripped env vars
  • 被禁用的平台 / Disabled platforms
  • 被剥离的 config 内嵌凭据 / Stripped config credentials

3. 平台设置独占警告 / Platform Settings Exclusive Warning

在 PlatformSettings 中为 6 个独占平台卡片顶部添加 NAlert 警告 / Added NAlert warning at top of 6 exclusive platform cards in PlatformSettings:

"此平台使用独占 token 锁。每个 profile 必须使用不同的身份 token,否则会与其他 profile 冲突。" "This platform uses exclusive token locking. Each profile must use a different identity token to avoid conflicts with other profiles."

EXCLUSIVE_PLATFORMS 列表来源 / List Source

精确对齐 hermes-agent 源码 gateway/platforms/*.py 中调用 _acquire_platform_lock 的 7 个 adapter。 Precisely aligned with the 7 adapters in hermes-agent's gateway/platforms/*.py that call _acquire_platform_lock.

验证方法 / Verification: grep -l _acquire_platform_lock ~/.hermes/hermes-agent/gateway/platforms/*.py

测试 / Tests

新增 tests/server/profile-credentials.test.ts(12 用例全过)/ Added 12 test cases (all passing):

  • isExclusivePlatformKey 命中 / 未命中边界 / hit/miss boundary cases (including removed aliases wechat/lark/line)
  • .env 文件剥离 + 备份 / .env stripping + backup
  • config.yaml 平台禁用 + 节点凭据清理 / config.yaml platform disable + credential stripping
  • 已 disabled 平台仍清理残留凭据 / Still strips credentials from already-disabled platforms (prevents identity reuse on re-enable)

复现步骤 / Reproduction Steps (before fix)

  1. 在 default profile .env 配置 WEIXIN_TOKEN 等独占凭据并启动 gateway / Configure WEIXIN_TOKEN etc. in default profile .env and start gateway
  2. web-ui → Profiles → New Profile → 勾选 Clone from current → 提交 / Check "Clone from current" → Submit
  3. 切换到新 profile 触发 gateway 启动 / Switch to new profile, triggering gateway start
  4. 报错 / Error: API Error 500: Gateway health check timed out after 15000ms

相关 / Related

文件变更 / File Changes

文件 / File变更 / Change
packages/server/src/services/hermes/profile-credentials.ts新增 / New - 智能克隆核心逻辑 / Smart clone core logic
packages/server/src/controllers/hermes/profiles.tsclone 后调用 smartCloneCleanup / Call smartCloneCleanup after clone
packages/client/src/api/hermes/profiles.tsCreateProfileResult 透传清理结果 / Pass through cleanup result
packages/client/src/stores/hermes/profiles.tsstore 透传 / Store passthrough
packages/client/src/components/hermes/profiles/ProfileCreateModal.vuetoast 显示清理摘要 / Toast cleanup summary
packages/client/src/components/hermes/settings/PlatformCard.vue新增 exclusive prop + NAlert / New exclusive prop + NAlert
packages/client/src/components/hermes/settings/PlatformSettings.vue6 个独占平台标记 exclusive / Mark 6 exclusive platforms
packages/client/src/i18n/locales/*.ts8 个 locale 新增 i18n key / 8 locales with new i18n keys
tests/server/profile-credentials.test.ts新增 / New - 12 个测试用例 / 12 test cases

Changed files

  • packages/client/src/api/hermes/profiles.ts (modified, +24/-4)
  • packages/client/src/components/hermes/profiles/ProfileCreateModal.vue (modified, +26/-4)
  • packages/client/src/components/hermes/settings/PlatformCard.vue (modified, +10/-1)
  • packages/client/src/components/hermes/settings/PlatformSettings.vue (modified, +7/-0)
  • packages/client/src/i18n/locales/de.ts (modified, +5/-0)
  • packages/client/src/i18n/locales/en.ts (modified, +5/-0)
  • packages/client/src/i18n/locales/es.ts (modified, +5/-0)
  • packages/client/src/i18n/locales/fr.ts (modified, +5/-0)
  • packages/client/src/i18n/locales/ja.ts (modified, +5/-0)
  • packages/client/src/i18n/locales/ko.ts (modified, +5/-0)
  • packages/client/src/i18n/locales/pt.ts (modified, +5/-0)
  • packages/client/src/i18n/locales/zh.ts (modified, +5/-0)
  • packages/client/src/stores/hermes/profiles.ts (modified, +3/-3)
  • packages/server/src/controllers/hermes/profiles.ts (modified, +41/-1)
  • packages/server/src/services/hermes/profile-credentials.ts (added, +187/-0)
  • tests/server/profile-credentials.test.ts (added, +205/-0)

PR #1: fix: resolve 7 identified issues [automated]

Description (problem / solution / changelog)

Resumo

Este PR corrige 7 issues identificados no repositório NousResearch/hermes-agent.


Issues Corrigidos

1. #17086 - custom endpoint com api_mode=anthropic_messages falhava com 404

Arquivo: agent/auxiliary_client.py Problema: Quando provider=custom com api_mode=anthropic_messages e base_url terminando em /anthropic, a funcao _resolve_provider_client() convertia a URL para o formato /v1/messages, causando 404 em provedores Anthropic-compatíveis de terceiros. Correcao: Mantem o path /anthropic quando api_mode=anthropic_messages e o base_url ja termina em /anthropic.


2. #17076 - kimi-coding vision quebrado (404 em analise de imagem)

Arquivo: agent/auxiliary_client.py Problema: kimi-coding nao estava listado em _PROVIDER_VISION_MODELS, entao auxiliary.vision.provider: auto nao conseguia detectar o modelo de visao disponivel para Kimi. Correcao: Adicionado kimi-coding e kimi-coding-cn ao mapa de modelos de visao.


3. #17080 - hermes profile create --clone copiava credenciais exclusivas de plataforma

Arquivo: hermes_cli/profiles.py Problema: O clone de perfil copiava TELEGRAM_BOT_TOKEN, DISCORD_BOT_TOKEN, WEIXIN_TOKEN verbatim. Quando dois perfis iniciam simultaneamente com o mesmo token, o adaptador de plataforma falha durante a aquisicao de lock. Correcao: Define _EXCLUSIVE_PLATFORM_KEYS e _EXCLUSIVE_PLATFORM_CONFIG_PATHS. Credenciais exclusivas sao comentadas em .env e entradas de plataforma sao enabled: false em config.yaml apos clone.


4. #17054 - Slack manifest rejeitava nomes com underscore

Arquivo: hermes_cli/commands.py Problema: _sanitize_slack_name() convertia nomes de comandos como _reload_mcp para Slack mas nao removia o prefixo underscore, causando rejeicao do manifest. Correcao: Adicionada verificacao para pular nomes que começam com underscore antes de adicionar a lista de slash commands.


5. #17057 - custom Kimi-compatible endpoint falhava apos tool call com thinking habilitado

Arquivo: run_agent.py Problema: _needs_kimi_tool_reasoning() só verificava hostnames oficiais (api.kimi.com, moonshot.ai, moonshot.cn). Endpoints Kimi-compatíveis customizados nao eram detectados. Correcao: Ampliada a verificacao para detectar endpoints customizados pela familia do modelo (nome contem "kimi" ou "k2") alem do hostname.


6. #17049 - UnicodeDecodeError no scan de processos Windows (wmic)

Arquivo: hermes_cli/gateway.py Problema: wmic emitia saida em encoding local do Windows (cp1252/utf-16), causando UnicodeDecodeError e AttributeError durante parsing. Correcao: O parsing agora usa errors=ignore no decode, tratando bytes invalidos como despreziveis.


7. #17052 - stale reasoning reutilizado quando turn atual nao tem reasoning_content

Arquivo: run_agent.py Problema: Mensagens de assistant com tool_calls e reasoning_content eram reutilizadas indevidamente em turns que nao tinham reasoning_content, causando confabulacoes em provedores como Qwen3.6:27b via Ollama. Correcao: O loop de replay agora detecta quando a mensagem atual e um assistant com tool_calls mas sem reasoning_content, e limpa msg[reasoning_content] e msg[reasoning] para evitar propagacao de estado de reasoning de turns anteriores.


Arquivos Modificados

  • agent/auxiliary_client.py - #17086, #17076
  • hermes_cli/profiles.py - #17080
  • hermes_cli/commands.py - #17054
  • run_agent.py - #17057, #17052
  • hermes_cli/gateway.py - #17049

Notas

  • Este PR contem 8 commits (7 issues + 1 fix de seguranca do upstream relacionado a redaction de secrets)
  • Todos os commits foram feitos na branch fix-7-issues-clean
  • Nenhum push intermediario foi feito - push unico ao final
  • Commits do upstream incluidos para manter o historico completo: #16843, #17041, #17039

Changed files

  • .gitignore (modified, +1/-0)
  • Dockerfile (modified, +6/-2)
  • acp_adapter/entry.py (modified, +11/-0)
  • acp_adapter/server.py (modified, +28/-1)
  • agent/anthropic_adapter.py (modified, +134/-74)
  • agent/auxiliary_client.py (modified, +325/-53)
  • agent/bedrock_adapter.py (modified, +41/-3)
  • agent/context_compressor.py (modified, +113/-5)
  • agent/credential_pool.py (modified, +82/-4)
  • agent/credential_sources.py (modified, +0/-1)
  • agent/error_classifier.py (modified, +32/-0)
  • agent/gemini_cloudcode_adapter.py (modified, +0/-2)
  • agent/gemini_schema.py (modified, +1/-1)
  • agent/google_code_assist.py (modified, +0/-1)
  • agent/google_oauth.py (modified, +3/-3)
  • agent/image_routing.py (added, +236/-0)
  • agent/memory_manager.py (modified, +113/-5)
  • agent/model_metadata.py (modified, +56/-21)
  • agent/nous_rate_guard.py (modified, +144/-1)
  • agent/onboarding.py (added, +191/-0)
  • agent/prompt_builder.py (modified, +38/-0)
  • agent/redact.py (modified, +13/-6)
  • agent/shell_hooks.py (modified, +7/-2)
  • agent/skill_commands.py (modified, +2/-2)
  • agent/title_generator.py (modified, +39/-5)
  • agent/transports/anthropic.py (modified, +1/-7)
  • agent/transports/chat_completions.py (modified, +74/-0)
  • agent/transports/codex.py (modified, +1/-3)
  • cli-config.yaml.example (modified, +28/-8)
  • cli.py (modified, +522/-195)
  • cron/jobs.py (modified, +34/-5)
  • cron/scheduler.py (modified, +39/-5)
  • docker/entrypoint.sh (modified, +9/-7)
  • flake.nix (modified, +1/-0)
  • gateway/channel_directory.py (modified, +67/-14)
  • gateway/config.py (modified, +84/-3)
  • gateway/display_config.py (modified, +3/-1)
  • gateway/mirror.py (modified, +57/-11)
  • gateway/pairing.py (modified, +2/-1)
  • gateway/platforms/__init__.py (modified, +2/-0)
  • gateway/platforms/base.py (modified, +233/-16)
  • gateway/platforms/discord.py (modified, +18/-24)
  • gateway/platforms/email.py (modified, +3/-0)
  • gateway/platforms/feishu_comment.py (modified, +0/-1)
  • gateway/platforms/helpers.py (modified, +11/-2)
  • gateway/platforms/matrix.py (modified, +493/-47)
  • gateway/platforms/mattermost.py (modified, +0/-1)
  • gateway/platforms/qqbot/adapter.py (modified, +2/-7)
  • gateway/platforms/slack.py (modified, +753/-70)
  • gateway/platforms/telegram.py (modified, +138/-14)
  • gateway/platforms/weixin.py (modified, +26/-3)
  • gateway/platforms/yuanbao.py (added, +4754/-0)
  • gateway/platforms/yuanbao_media.py (added, +645/-0)
  • gateway/platforms/yuanbao_proto.py (added, +1209/-0)
  • gateway/platforms/yuanbao_sticker.py (added, +558/-0)
  • gateway/run.py (modified, +1143/-283)
  • gateway/runtime_footer.py (added, +150/-0)
  • gateway/session.py (modified, +16/-21)
  • gateway/stream_consumer.py (modified, +110/-0)
  • gateway/whatsapp_identity.py (modified, +21/-1)
  • hermes_cli/auth.py (modified, +40/-4)
  • hermes_cli/azure_detect.py (modified, +1/-1)
  • hermes_cli/backup.py (modified, +272/-1)
  • hermes_cli/banner.py (modified, +0/-1)
  • hermes_cli/claw.py (modified, +67/-6)
  • hermes_cli/commands.py (modified, +119/-5)
  • hermes_cli/config.py (modified, +322/-29)
  • hermes_cli/debug.py (modified, +13/-7)
  • hermes_cli/dingtalk_auth.py (modified, +0/-1)
  • hermes_cli/doctor.py (modified, +11/-1)
  • hermes_cli/env_loader.py (modified, +2/-1)
  • hermes_cli/fallback_cmd.py (added, +361/-0)
  • hermes_cli/gateway.py (modified, +47/-12)
  • hermes_cli/hooks.py (modified, +1/-2)
  • hermes_cli/main.py (modified, +691/-58)
  • hermes_cli/model_catalog.py (added, +329/-0)
  • hermes_cli/model_switch.py (modified, +55/-6)
  • hermes_cli/models.py (modified, +251/-43)
  • hermes_cli/nous_subscription.py (modified, +16/-8)
  • hermes_cli/oneshot.py (modified, +28/-11)
  • hermes_cli/platforms.py (modified, +1/-0)
  • hermes_cli/plugins.py (modified, +14/-0)
  • hermes_cli/plugins_cmd.py (modified, +0/-1)
  • hermes_cli/profiles.py (modified, +199/-4)
  • hermes_cli/providers.py (modified, +26/-0)
  • hermes_cli/runtime_provider.py (modified, +100/-14)
  • hermes_cli/setup.py (modified, +69/-16)
  • hermes_cli/skills_hub.py (modified, +230/-20)
  • hermes_cli/slack_cli.py (added, +152/-0)
  • hermes_cli/status.py (modified, +3/-2)
  • hermes_cli/timeouts.py (modified, +4/-4)
  • hermes_cli/tips.py (modified, +2/-3)
  • hermes_cli/tools_config.py (modified, +173/-4)
  • hermes_cli/web_server.py (modified, +11/-14)
  • hermes_cli/webhook.py (modified, +2/-2)
  • hermes_logging.py (modified, +3/-4)
  • hermes_state.py (modified, +578/-164)
  • model_tools.py (modified, +45/-10)
  • nix/checks.nix (modified, +30/-3)
  • nix/hermes-agent.nix (added, +186/-0)

PR #17090: fix: resolve 7 identified issues [automated]

Description (problem / solution / changelog)

Resumo

Este PR corrige 7 issues identificados no repositório NousResearch/hermes-agent.


Issues Corrigidos

1. #17086 - custom endpoint com api_mode=anthropic_messages falhava com 404

Arquivo: agent/auxiliary_client.py Problema: Quando provider=custom com api_mode=anthropic_messages e base_url terminando em /anthropic, a funcao _resolve_provider_client() convertia a URL para o formato /v1/messages, causando 404 em provedores Anthropic-compatíveis de terceiros. Correcao: Mantem o path /anthropic quando api_mode=anthropic_messages e o base_url ja termina em /anthropic.


2. #17076 - kimi-coding vision quebrado (404 em analise de imagem)

Arquivo: agent/auxiliary_client.py Problema: kimi-coding nao estava listado em _PROVIDER_VISION_MODELS, entao auxiliary.vision.provider: auto nao conseguia detectar o modelo de visao disponivel para Kimi. Correcao: Adicionado kimi-coding e kimi-coding-cn ao mapa de modelos de visao.


3. #17080 - hermes profile create --clone copiava credenciais exclusivas de plataforma

Arquivo: hermes_cli/profiles.py Problema: O clone de perfil copiava TELEGRAM_BOT_TOKEN, DISCORD_BOT_TOKEN, WEIXIN_TOKEN verbatim. Quando dois perfis iniciam simultaneamente com o mesmo token, o adaptador de plataforma falha durante a aquisicao de lock. Correcao: Define _EXCLUSIVE_PLATFORM_KEYS e _EXCLUSIVE_PLATFORM_CONFIG_PATHS. Credenciais exclusivas sao comentadas em .env e entradas de plataforma sao enabled: false em config.yaml apos clone.


4. #17054 - Slack manifest rejeitava nomes com underscore

Arquivo: hermes_cli/commands.py Problema: _sanitize_slack_name() convertia nomes de comandos como _reload_mcp para Slack mas nao removia o prefixo underscore, causando rejeicao do manifest. Correcao: Adicionada verificacao para pular nomes que começam com underscore antes de adicionar a lista de slash commands.


5. #17057 - custom Kimi-compatible endpoint falhava apos tool call com thinking habilitado

Arquivo: run_agent.py Problema: _needs_kimi_tool_reasoning() só verificava hostnames oficiais (api.kimi.com, moonshot.ai, moonshot.cn). Endpoints Kimi-compatíveis customizados nao eram detectados. Correcao: Ampliada a verificacao para detectar endpoints customizados pela familia do modelo (nome contem "kimi" ou "k2") alem do hostname.


6. #17049 - UnicodeDecodeError no scan de processos Windows (wmic)

Arquivo: hermes_cli/gateway.py Problema: wmic emitia saida em encoding local do Windows (cp1252/utf-16), causando UnicodeDecodeError e AttributeError durante parsing. Correcao: O parsing agora usa errors=ignore no decode, tratando bytes invalidos como despreziveis.


7. #17052 - stale reasoning reutilizado quando turn atual nao tem reasoning_content

Arquivo: run_agent.py Problema: Mensagens de assistant com tool_calls e reasoning_content eram reutilizadas indevidamente em turns que nao tinham reasoning_content, causando confabulacoes em provedores como Qwen3.6:27b via Ollama. Correcao: O loop de replay agora detecta quando a mensagem atual e um assistant com tool_calls mas sem reasoning_content, e limpa msg[reasoning_content] e msg[reasoning] para evitar propagacao de estado de reasoning de turns anteriores.


Arquivos Modificados

  • agent/auxiliary_client.py - #17086, #17076
  • hermes_cli/profiles.py - #17080
  • hermes_cli/commands.py - #17054
  • run_agent.py - #17057, #17052
  • hermes_cli/gateway.py - #17049

Notas

  • Este PR contem 8 commits (7 issues + 1 fix de seguranca do upstream relacionado a redaction de secrets)
  • Todos os commits foram feitos na branch fix-7-issues-clean
  • Nenhum push intermediario foi feito - push unico ao final
  • Commits do upstream incluidos para manter o historico completo: #16843, #17041, #17039

Changed files

  • agent/auxiliary_client.py (modified, +36/-0)
  • agent/redact.py (modified, +6/-3)
  • gateway/run.py (modified, +10/-4)
  • hermes_cli/commands.py (modified, +3/-0)
  • hermes_cli/gateway.py (modified, +22/-8)
  • hermes_cli/main.py (modified, +18/-7)
  • hermes_cli/profiles.py (modified, +141/-2)
  • run_agent.py (modified, +33/-9)
  • tools/terminal_tool.py (modified, +10/-2)

Code Example

API Error 500: Gateway health check timed out after 15000ms

---

hermes profile create profile-a   # Initial profile, configure Weixin
# Set WEIXIN_TOKEN=xxx, WEIXIN_ACCOUNT_ID=yyy in profile-a/.env
# Set platforms.weixin.enabled: true in profile-a/config.yaml

hermes profile create profile-b --clone   # Clone
# profile-b/.env also has WEIXIN_TOKEN=xxx
# profile-b/config.yaml also has platforms.weixin.enabled: true

hermes gateway start profile-a   # OK
hermes gateway start profile-b   # Fails: health check timeout

---

hermes profile create profile-b --clone --exclude-platform-credentials
RAW_BUFFERClick to expand / collapse

Symptom

After cloning an existing profile with hermes profile create <new> --clone, starting gateways for multiple profiles simultaneously causes the later gateway to fail health checks:

API Error 500: Gateway health check timed out after 15000ms

In hermes-web-ui, this surfaces as a "profile loading error" that is difficult to diagnose.

Root Cause

--clone currently performs a full copy: it copies the source profile's .env and config.yaml verbatim into the new profile directory. This means:

  • WEIXIN_TOKEN / WEIXIN_ACCOUNT_ID / TELEGRAM_BOT_TOKEN / DISCORD_BOT_TOKEN / SLACK_APP_TOKEN / SIGNAL_PHONE_NUMBER and other exclusive platform credentials are copied as-is
  • config.yaml entries like platforms.<name>.enabled: true are preserved
  • Embedded credentials in config.yaml (e.g., platforms.weixin.token, platforms.weixin.extra.account_id) are also copied

Exclusive platform credentials are fundamentally "one-to-one identity bindings":

  • A Weixin token maps to exactly one bot instance
  • A Telegram bot token can only getUpdates from one process at a time
  • Discord/Slack long-lived connections are mutually exclusive
  • Signal, WhatsApp Business, etc. follow the same pattern

When multiple profiles hold the same token and start simultaneously, hermes-agent's platform adapter fails during initialization or scoped lock acquisition (the scoped lock mechanism is described in #4587). The error is swallowed by the gateway process; the outer layer only sees a health check timeout.

Reproduction

hermes profile create profile-a   # Initial profile, configure Weixin
# Set WEIXIN_TOKEN=xxx, WEIXIN_ACCOUNT_ID=yyy in profile-a/.env
# Set platforms.weixin.enabled: true in profile-a/config.yaml

hermes profile create profile-b --clone   # Clone
# profile-b/.env also has WEIXIN_TOKEN=xxx
# profile-b/config.yaml also has platforms.weixin.enabled: true

hermes gateway start profile-a   # OK
hermes gateway start profile-b   # Fails: health check timeout

Expected Behavior

Option A: Exclude exclusive credentials during clone (recommended)

New CLI flag:

hermes profile create profile-b --clone --exclude-platform-credentials

Or exclude by default (breaking change, but aligns with the semantics "clone = reuse model/tool config, not identity"):

  • When copying .env, skip keys matching ^(TELEGRAM|DISCORD|SLACK|WHATSAPP|SIGNAL|WEIXIN|FEISHU)_ (aligned with the 7 adapters in gateway/platforms/*.py that call _acquire_platform_lock)
  • When copying config.yaml, force platforms.<exclusive>.enabled to false
  • Also strip embedded credentials under exclusive platform nodes in config.yaml (e.g., platforms.weixin.token, platforms.weixin.extra.account_id, platforms.telegram.bot_token) to prevent reuse of the source profile's identity when the user later re-enables the platform
  • CLI output should clearly state which keys were skipped, prompting the user to configure them separately in the new profile

Model provider API keys (OPENAI_API_KEY / ANTHROPIC_API_KEY etc.) and tool config (BROWSER_HEADLESS / TERMINAL_DEFAULT_SHELL) should still be copied — they are safely shareable.

Option B: Graceful degradation on token conflict

When gateway startup detects that a token is already held by another profile:

  • Do not kill the entire gateway
  • Only disable the conflicting platform and log a warning: platform 'weixin' disabled: token already in use by profile 'profile-a'
  • Other platforms and model functionality start normally

This way, even without --exclude-platform-credentials, a single platform conflict does not render the entire profile unusable.

Current web-ui Workaround

hermes-web-ui has implemented a server-side "smart clone": after calling hermes profile create <name> --clone, it immediately strips exclusive credentials from the new profile's .env, disables corresponding platform nodes in config.yaml (with backups). See hermes-web-ui PR #283.

However, this is an upper-layer patch — the CLI's default behavior is unchanged, so command-line users and other integrations (e.g., custom scripts) will still hit this issue. A fix at the hermes-agent level would be ideal.

Related

  • web-ui implementation: packages/server/src/services/hermes/profile-credentials.ts
  • Exclusive platform list (source: grep -l _acquire_platform_lock gateway/platforms/*.py): telegram, discord, slack, whatsapp, signal, weixin, feishu

Cross-references in this repo

  • #4587 Multi-profile gateway is not safe: kill_gateway_processes() and release_all_scoped_locks() are profile-blind — describes the same multi-profile coexistence challenge, mentions "second profile to lose its Telegram token lock, leading to duplicate polling or fatal errors", confirming the token exclusion lock mechanism
  • #10376 Profile isolation is incomplete: --clone copies memory — also about incomplete --clone behavior, but focuses on memory files + cross-profile file access; that issue author considers .env copying to be expected behavior, while this issue argues the opposite for exclusive platform credentials
  • #8553 fix: improve profile creation UX — seed SOUL.md + credential warning (merged) — added credential warning for non-clone path, but clone path is still untouched

extent analysis

TL;DR

To fix the issue, exclude exclusive platform credentials during the clone process using the --exclude-platform-credentials flag or by modifying the hermes profile create command to exclude these credentials by default.

Guidance

  • When cloning a profile, use the --exclude-platform-credentials flag to prevent copying exclusive platform credentials, such as WEIXIN_TOKEN and TELEGRAM_BOT_TOKEN.
  • Modify the hermes profile create command to exclude exclusive platform credentials by default, by skipping keys matching ^(TELEGRAM|DISCORD|SLACK|WHATSAPP|SIGNAL|WEIXIN|FEISHU)_ in .env and forcing platforms.<exclusive>.enabled to false in config.yaml.
  • Consider implementing a graceful degradation mechanism that disables conflicting platforms and logs a warning when a token conflict is detected during gateway startup.
  • Review the related issues, such as #4587 and #10376, to ensure that the fix addresses the underlying multi-profile coexistence challenges.

Example

hermes profile create profile-b --clone --exclude-platform-credentials

This command clones a profile while excluding exclusive platform credentials.

Notes

The fix should be applied at the hermes-agent level to ensure that all users, including command-line users and integrations, are affected. The current web-ui workaround is an upper-layer patch that only addresses the issue for web-ui users.

Recommendation

Apply the workaround by using the --exclude-platform-credentials flag when cloning a profile, as this is a non-breaking change that can be implemented immediately. A more permanent fix would involve modifying the hermes profile create command to exclude exclusive platform credentials by default, but this may require additional testing and validation.

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…

Still need to ship something?

×6

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

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix `hermes profile create --clone` copies exclusive platform credentials causing multi-profile gateway conflicts [3 pull requests, 1 participants]