Bug type: Regression (worked before, now fails)
Summary
After updating to 2026.6.11, which externalizes the previously-bundled Mattermost plugin to @openclaw/mattermost, all native /oc_* slash commands return 401 Unauthorized: invalid command token. on every invocation. Normal DMs and @mentions work fine — only native slash command callbacks are rejected. Slash commands worked correctly on 2026.6.10 with an identical config.
Environment
- OpenClaw 2026.6.11 (e085fa1)
@openclaw/mattermost 2026.6.11 (externalized; installed to ~/.openclaw/npm/projects/openclaw-mattermost-*)
- Codex app-server harness, macOS (Apple Silicon), single Mattermost account (
default)
- Callback via HTTPS reverse proxy:
callbackUrl: https://<host>/api/channels/mattermost/command
What I've confirmed
- The POST reaches OpenClaw. Reverse-proxy access log shows each slash callback forwarded to the gateway; the gateway itself returns 401 (not the proxy). Requests returned
200 until the 6.11 update, 401 immediately after.
- All Mattermost-side command fields are correct. For each command, MM has
method: "P", correct url, team_id, trigger, and a present token — satisfying every field check in validateMattermostSlashCommandToken (slash-state-*.js, ~line 1660).
- A clean re-registration does not fix it. With the gateway stopped, I deleted all
oc_* commands via the MM API (verified 0 remain), then started the gateway. OpenClaw created all 48 commands fresh (logged registered command /oc_status (id=…)) with matching id/token/method/url. Callbacks still 401.
- Toggling
commands.native false→restart→true→restart does not help (commands are reused as "already registered").
Handler path
Installed 6.11 handler dist/slash-state-*.js returns 401 at three points; the relevant one for a valid inbound command is the token/command resolution in resolveSlashHandlerForToken (checks state.commandTokens.has(token)) with fallback to resolveSlashHandlerForCommand. Both apparently miss despite a fresh registration, so the in-memory commandTokens/registeredCommands set for the account does not match the token Mattermost transmits in the callback body.
What I could not capture
Byte-level comparison of the token in the live callback POST body vs. OpenClaw's in-memory commandTokens — the host is a standard (non-root) user, so packet capture isn't available, and I did not want to run a code shim on a production gateway. A maintainer able to log the inbound payload.token alongside the resolved account's commandTokens at the 401 branch should see the mismatch directly.
Suspected cause
Regression in the externalized @openclaw/mattermost 6.11 registration/token-set population path. (Externalization regressions have precedent in this range, e.g. #95658.)
Config (relevant)
"commands": { "native": true, "nativeSkills": true,
"callbackUrl": "https://<host>/api/channels/mattermost/command",
"callbackPath": "/api/channels/mattermost/command" }
Bug type: Regression (worked before, now fails)
Summary
After updating to 2026.6.11, which externalizes the previously-bundled Mattermost plugin to
@openclaw/mattermost, all native/oc_*slash commands return401 Unauthorized: invalid command token.on every invocation. Normal DMs and @mentions work fine — only native slash command callbacks are rejected. Slash commands worked correctly on 2026.6.10 with an identical config.Environment
@openclaw/mattermost2026.6.11 (externalized; installed to~/.openclaw/npm/projects/openclaw-mattermost-*)default)callbackUrl: https://<host>/api/channels/mattermost/commandWhat I've confirmed
200until the 6.11 update,401immediately after.method: "P", correcturl,team_id,trigger, and a presenttoken— satisfying every field check invalidateMattermostSlashCommandToken(slash-state-*.js, ~line 1660).oc_*commands via the MM API (verified 0 remain), then started the gateway. OpenClaw created all 48 commands fresh (loggedregistered command /oc_status (id=…)) with matching id/token/method/url. Callbacks still 401.commands.nativefalse→restart→true→restart does not help (commands are reused as "already registered").Handler path
Installed 6.11 handler
dist/slash-state-*.jsreturns 401 at three points; the relevant one for a valid inbound command is the token/command resolution inresolveSlashHandlerForToken(checksstate.commandTokens.has(token)) with fallback toresolveSlashHandlerForCommand. Both apparently miss despite a fresh registration, so the in-memorycommandTokens/registeredCommandsset for the account does not match the token Mattermost transmits in the callback body.What I could not capture
Byte-level comparison of the token in the live callback POST body vs. OpenClaw's in-memory
commandTokens— the host is a standard (non-root) user, so packet capture isn't available, and I did not want to run a code shim on a production gateway. A maintainer able to log the inboundpayload.tokenalongside the resolved account'scommandTokensat the 401 branch should see the mismatch directly.Suspected cause
Regression in the externalized
@openclaw/mattermost6.11 registration/token-set population path. (Externalization regressions have precedent in this range, e.g. #95658.)Config (relevant)