← Back to PRs

#17070: fix(telegram): Outbound: ignore empty legacy target fields

by yhw2003 open 2026-02-15 11:07 View on GitHub →
stale size: S
## Summary - Problem: `message` tool calls could include `target` plus empty legacy fields (`to: ""` / `channelId: ""`), and `applyTargetToParams` treated any string (including empty) as legacy conflict, throwing `Use \`target\` instead of \`to\`/\`channelId\`.`. - Why it matters: image/file sends failed before dispatch on multiple channels (reported on telegram), even when `target` was valid. #5364 - What changed: in `src/infra/outbound/channel-target.ts`, legacy-field detection now only triggers for **non-empty** `to/channelId`; strict rejection for non-empty legacy usage is preserved. - What did NOT change (scope boundary): no channel plugin behavior changed; no media hydration, routing, or gateway transport logic changed. ## Change Type (select all) - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Docs - [ ] Security hardening - [ ] Chore/infra ## Scope (select all touched areas) - [x] Gateway / orchestration - [x] Skills / tool execution - [ ] Auth / tokens - [ ] Memory / storage - [x] Integrations - [ ] API / contracts - [ ] UI / DX - [ ] CI/CD / infra ## Linked Issue/PR #5364 #5608 ## User-visible / Behavior Changes - Sending media/files no longer fails with `Use \`target\` instead of \`to\`/\`channelId\`.` when `target` is present and legacy fields are empty strings. - Non-empty legacy `to/channelId` is still rejected. ## Security Impact (required) - New permissions/capabilities? (`No`) - Secrets/tokens handling changed? (`No`) - New/changed network calls? (`No`) - Command/tool execution surface changed? (`No`) - Data access scope changed? (`No`) - If any `Yes`, explain risk + mitigation: `N/A` ## Repro + Verification ### Environment - OS: Linux (Arch) - Runtime/container: Node `v22.17.1, pnpm openclaw --dev gateway - Relevant config (redacted): standard local dev config, no special overrides required for repro ### Steps 1. Run `pnpm openclaw --dev gateway --force --verbose`. 2. Trigger a `message` tool call to send image/file with `action: "send"` and valid `target`. 3. Observe payload may contain `channelId: ""` (or `to: ""`) from model-generated optional fields. ### Expected - Request should proceed using `target` and send normally. ### Actual - Before fix: throws `Use \`target\` instead of \`to\`/\`channelId\`.` before dispatch. - After fix: empty legacy fields no longer trip validation; non-empty legacy fields still rejected. ## Evidence Attach at least one: - [x] Failing test/log before + passing after - [x] Trace/log snippets - [ ] Screenshot/recording - [ ] Perf numbers (if relevant) Evidence pointers: - Failing tool-call payload + error: ``` 13:10:38 tools: message failed stack: Error: Use `target` instead of `to`/`channelId`. at applyTargetToParams (file:///home/yhw/Develop/openclaw/dist/reply-CX2QH7tO.js:35079:48) at runMessageAction (file:///home/yhw/Develop/openclaw/dist/reply-CX2QH7tO.js:40119:2) at execute (file:///home/yhw/Develop/openclaw/dist/reply-CX2QH7tO.js:40486:25) at execute (file:///home/yhw/Develop/openclaw/dist/reply-CX2QH7tO.js:44602:17) at processTicksAndRejections (node:internal/process/task_queues:105:5) at Object.execute (file:///home/yhw/Develop/openclaw/dist/reply-CX2QH7tO.js:44526:11) at Object.execute (file:///home/yhw/Develop/openclaw/dist/reply-CX2QH7tO.js:47026:21) at Object.execute (file:///home/yhw/Develop/openclaw/node_modules/.pnpm/@mariozechner+pi-coding-agent@0.52.12_ws@8.19.0_zod@4.3.6/node_modules/@mariozechner/pi-coding-agent/src/core/extensions/wrapper.ts:71:20) at executeToolCalls (file:///home/yhw/Develop/openclaw/node_modules/.pnpm/@mariozechner+pi-agent-core@0.52.12_ws@8.19.0_zod@4.3.6/node_modules/@mariozechner/pi-agent-core/src/agent-loop.ts:324:13) at runLoop (file:///home/yhw/Develop/openclaw/node_modules/.pnpm/@mariozechner+pi-agent-core@0.52.12_ws@8.19.0_zod@4.3.6/node_modules/@mariozechner/pi-agent-core/src/agent-loop.ts:157:27) at file:///home/yhw/Develop/openclaw/node_modules/.pnpm/@mariozechner+pi-agent-core@0.52.12_ws@8.19.0_zod@4.3.6/node_modules/@mariozechner/pi-agent-core/src/agent-loop.ts:51:3 ``` ## Human Verification (required) What you personally verified (not just CI), and how: - Verified scenarios: reproduced failing path from session log payload; confirmed root cause in `applyTargetToParams`; verified updated condition logic and targeted vitest pass. - Edge cases checked: non-empty legacy `channelId/to` still throws; empty legacy fields do not throw. - What you did **not** verify: end-to-end live media send on each real channel account after patch. ## Compatibility / Migration - Backward compatible? (`Yes`) - Config/env changes? (`No`) - Migration needed? (`No`) - If yes, exact upgrade steps: `N/A` ## Failure Recovery (if this breaks) - How to disable/revert this change quickly: revert commit `f1280266f`. - Files/config to restore: `src/infra/outbound/channel-target.ts`. - Known bad symptoms reviewers should watch for: legacy non-empty `to/channelId` no longer being rejected (should still be rejected). ## Risks and Mitigations - Risk: relaxing legacy detection could accidentally allow malformed legacy input. - Mitigation: only empty-string legacy values are ignored; non-empty legacy fields continue to hard-fail, preserving strict contract. <!-- greptile_comment --> <h3>Greptile Summary</h3> Fixes a bug where `applyTargetToParams` in `channel-target.ts` incorrectly threw `"Use \`target\` instead of \`to\`/\`channelId\`."` when model-generated tool calls included a valid `target` alongside empty-string legacy fields (`to: ""` or `channelId: ""`). The fix narrows legacy-field detection to only trigger on **non-empty** strings, which is consistent with how the caller (`runMessageAction` in `message-action-runner.ts`) already handles legacy fields using the same `.trim().length > 0` pattern. - `src/infra/outbound/channel-target.ts`: `hasLegacyTo` and `hasLegacyChannelId` now require the string value to be non-empty after trimming, matching the existing convention in `runMessageAction` and `actionHasTarget`. - `appcast.xml`: Routine release metadata update (adds 2026.2.15, removes 2026.2.12). <h3>Confidence Score: 5/5</h3> - This PR is safe to merge — it's a minimal, well-scoped bug fix that aligns with existing codebase patterns. - The change is a two-line fix that narrows empty-string detection to match the convention already used by the calling code (`runMessageAction`) and sibling function (`actionHasTarget`). Non-empty legacy fields still trigger the same strict rejection. The logic is straightforward and the risk of regression is very low. - No files require special attention. <sub>Last reviewed commit: 87147ee</sub> <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs