#13489: fix: preserve Slack channel/user ID case in target normalization
channel: slack
stale
Cluster:
Slack Integration Improvements
## Summary
Fixes #4751 and #10668.
Slack channel and user IDs are case-sensitive (e.g. `C0ADKTLND7U`, `U025D9U8U`). The `normalizeTargetId()` function was lowercasing the entire target string (`channel:C0ADKTLND7U` → `channel:c0adktlnd7u`), causing Slack API calls (reactions, sends, reads) to fail with `Unknown channel`.
## Changes
### 1. `src/channels/targets.ts` — `normalizeTargetId()`
Only lowercase the kind prefix, preserve the ID:
```diff
- return \`\${kind}:\${id}\`.toLowerCase();
+ return \`\${kind.toLowerCase()}:\${id}\`;
```
### 2. `src/infra/outbound/target-normalization.ts` — `normalizeTargetForProvider()`
Remove `.toLowerCase()` from the fallback path:
```diff
- plugin?.messaging?.normalizeTarget?.(raw) ?? (raw.trim().toLowerCase() || undefined);
+ plugin?.messaging?.normalizeTarget?.(raw) ?? (raw.trim() || undefined);
```
### 3. `src/infra/outbound/message-action-runner.ts` — Slack ID auto-correction
When an agent passes a Slack channel ID (like `C0ADKTLND7U`) as the `channel` parameter (which expects a provider name like `slack`), auto-detect and reinterpret:
- Adds `isSlackChannelHint()` to detect Slack-style IDs
- When in Slack context, rewrites `channel` to `"slack"` and moves the ID to `target`
## Tests
- Updated existing Slack target tests to expect preserved case
- Added new `target-normalization.test.ts` with Slack case-preservation tests
- Added regression test for Slack IDs passed as `channel` parameter
- All 38 related tests pass ✅
## Notes
- Discord IDs are numeric and unaffected by case — no regression there
- The `preserveTargetCase()` function in `target-resolver.ts` was a partial workaround for Slack but only covered certain paths; this fix addresses the root cause
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR adjusts target normalization to preserve case for Slack channel/user IDs, fixing failures caused by lowercasing IDs during normalization. Specifically:
- `normalizeTargetId()` now lowercases only the kind prefix (`channel:`/`user:`) while keeping the ID unchanged.
- `normalizeTargetForProvider()` no longer lowercases inputs on the fallback path when no plugin normalizer is available.
- `runMessageAction()` adds a Slack-context heuristic to treat Slack-looking IDs accidentally passed via the `channel` param as the action target, and forces `channel` to `slack`.
The changes align normalization behavior with Slack’s case-sensitive identifiers and extend test coverage for Slack case preservation and the new heuristic rewrite behavior.
<h3>Confidence Score: 4/5</h3>
- Mostly safe to merge, but the Slack-channel heuristic rewrite should be tightened to avoid overriding explicit channel selection in ambiguous cases.
- Core normalization changes are small and well-covered by tests. The main risk is the new `channel`→`slack` rewrite based on a regex + Slack context, which mutates params before resolution/policy enforcement and could redirect actions unexpectedly when `channel` contains a Slack-looking string.
- src/infra/outbound/message-action-runner.ts
<!-- greptile_other_comments_section -->
<sub>(4/5) You can add custom instructions or style guidelines for the agent [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#22221: fix: preserve case of target ID in normalizeTargetId
by zerone0x · 2026-02-20
91.2%
#8024: fix(slack): resolve channel names via directory for cross-account m...
by emma-digital-assistant · 2026-02-03
85.9%
#10643: fix(slack): classify D-prefix DMs correctly when channel_type disag...
by mcaxtr · 2026-02-06
82.0%
#19430: Slack: infer bare user targets before media upload
by gg2uah · 2026-02-17
81.9%
#13881: fix: Address Greptile feedback - test isolation and channel resolution
by trevorgordon981 · 2026-02-11
78.9%
#22101: fix(slack): dedupe mentions by ts fallback for app_mention
by AIflow-Labs · 2026-02-20
78.8%
#4878: fix: string/type handling and API fixes (#4537, #4380, #4373, #4547...
by lailoo · 2026-01-30
78.4%
#11491: feat(slack): allow agents to resolve channel IDs to names and list ...
by Lukavyi · 2026-02-07
78.2%
#8684: fix(slack): add title param and channel resolution for file upload
by shuans · 2026-02-04
78.1%
#16562: fix(signal): preserve case for group and username targets
by akalypse · 2026-02-14
77.8%