Skip to content

fix(tools): strip arg_value XML suffix from exec/read args#48792

Open
zerone0x wants to merge 1 commit intoopenclaw:mainfrom
zerone0x:fix/strip-xml-arg-value-suffix
Open

fix(tools): strip arg_value XML suffix from exec/read args#48792
zerone0x wants to merge 1 commit intoopenclaw:mainfrom
zerone0x:fix/strip-xml-arg-value-suffix

Conversation

@zerone0x
Copy link
Copy Markdown
Contributor

Summary

Fixes #48780

Some models (e.g. Qwen via DashScope) emit tool call arguments with trailing XML fragments like </arg_value>> appended to string values. This corrupts exec() commands (e.g. echo "test" becomes echo "test</arg_value>>) and read() file paths, blocking all file operations and command execution on affected platforms.

Changes

  • src/agents/pi-tools.params.ts: Add stripXmlArgValueSuffix() function and stripXmlArgValueSuffixFromRecord() helper that strips </arg_value> with optional trailing > characters from all string values in tool call argument records. Called from normalizeToolParams() which covers read, write, and edit tools.
  • src/agents/bash-tools.exec.ts: Import and apply stripXmlArgValueSuffix() to the command parameter in the exec tool's execute handler, since exec does not go through normalizeToolParams.
  • src/agents/pi-tools.params.test.ts: Add unit tests covering the suffix stripping for various patterns and integration with normalizeToolParams.

Root cause

Models using XML-based internal tool calling formats (common with Qwen/DashScope) sometimes fail to properly separate the argument value from the XML closing tag, resulting in </arg_value>> being concatenated to the end of string argument values. This is a provider-side bug, but we can defensively strip these artifacts at the parameter normalization layer.

Test plan

  • All 11 new unit tests pass
  • TypeScript type check passes
  • Existing lint/format checks pass

Some models (e.g. Qwen via DashScope) emit tool call arguments with
trailing XML fragments like `</arg_value>>` appended to string values.
This corrupts exec commands and file paths, blocking all file operations
and command execution on affected platforms.

Add `stripXmlArgValueSuffix` to strip these artifacts from tool call
argument string values in both the parameter normalization layer (for
read/write/edit tools) and the exec tool's command parameter.

Fixes openclaw#48780

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Mar 17, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 71c6c921fd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +105 to +109
function stripXmlArgValueSuffixFromRecord(record: Record<string, unknown>): void {
for (const key of Object.keys(record)) {
const value = record[key];
if (typeof value === "string" && value.includes("</arg_value>")) {
record[key] = stripXmlArgValueSuffix(value);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restrict XML suffix stripping to path/command fields

The new record-wide sanitization mutates every string parameter, so write/edit payloads can be silently corrupted when content, oldText, or newText legitimately end with </arg_value> (or </arg_value>>). Because normalizeToolParams is used by the write/edit wrappers, this truncates user-requested file content and can also break edit matching for valid XML/text inputs; the stripping should be scoped to known affected fields (e.g. command/path aliases) instead of all string keys.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 17, 2026

Greptile Summary

This PR adds a defensive sanitization layer that strips malformed </arg_value> XML-tag suffixes emitted by certain model providers (e.g. Qwen/DashScope) from tool call arguments, preventing corrupted exec commands and file paths. The fix is applied in two places: via stripXmlArgValueSuffixFromRecord inside normalizeToolParams (covering read/write/edit), and via a direct stripXmlArgValueSuffix call on params.command in the exec handler.

  • Correct integration points: read/write/edit tools are covered through normalizeToolParams; exec command is handled directly since exec bypasses that normalizer.
  • Regex quantifier is slightly narrow: >>{0,2}$ handles only 1–3 trailing > characters; using >*$ would defensively handle any count.
  • Partial exec coverage: only params.command is sanitized in bash-tools.exec.ts; other string exec parameters (workdir, host, security, ask, node) receive no equivalent treatment and could be corrupted by the same provider bug.
  • Test clarity: the normalizeToolParams test labelled "strips from command param" exercises a code path that doesn't match the actual exec flow (exec calls stripXmlArgValueSuffix directly), which could mislead future readers, though the assertion itself is valid.

Confidence Score: 4/5

  • Safe to merge — the fix correctly addresses the documented issue with minimal risk of regression.
  • The core logic is sound: the suffix strip is anchored to the end of the string, the fast-path includes guard prevents unnecessary regex work, and all the common corrupted forms are handled. Two minor gaps exist — the regex quantifier caps at 3 > chars and exec only sanitizes command — but neither is likely to affect real-world usage given the known provider behaviour described in the issue.
  • src/agents/bash-tools.exec.ts — only command is sanitized; other string params are uncovered.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/agents/pi-tools.params.ts
Line: 96

Comment:
**Regex quantifier may leave residual `>` for unusual provider output**

The pattern `>>{0,2}$` matches `</arg_value>` followed by 0, 1, or 2 additional `>` characters — handling 1, 2, or 3 total `>` after `arg_value`. If a particularly malformed provider response happens to emit four or more trailing `>` (e.g. `</arg_value>>>>>`), the regex will only strip `</arg_value>>>` and leave the remainder in the value.

Using `>*` (zero-or-more) instead of `>{0,2}` would handle any number of trailing `>` characters without changing behavior for the documented cases:

```suggestion
const XML_ARG_VALUE_SUFFIX_RE = /<\/arg_value>>*$/;
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/agents/bash-tools.exec.ts
Line: 230-232

Comment:
**Other string exec params not sanitized**

Only `params.command` is stripped, but the exec tool also accepts other string-typed parameters — `workdir`, `host`, `security`, `ask`, and `node` — that could be equally affected if the provider leaks the `</arg_value>` artifact into those values. A malformed `workdir` would silently break the working-directory resolution, which is hard to debug.

Consider sanitizing the full set of string exec params, or centralising via a shared helper similar to `stripXmlArgValueSuffixFromRecord`:

```typescript
// Strip all string exec params, not just command
for (const key of ["command", "workdir", "host", "security", "ask", "node"] as const) {
  if (typeof params[key] === "string") {
    params[key] = stripXmlArgValueSuffix(params[key]!);
  }
}
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 71c6c92

*
* @see https://github.com/openclaw/openclaw/issues/48780
*/
const XML_ARG_VALUE_SUFFIX_RE = /<\/arg_value>>{0,2}$/;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Regex quantifier may leave residual > for unusual provider output

The pattern >>{0,2}$ matches </arg_value> followed by 0, 1, or 2 additional > characters — handling 1, 2, or 3 total > after arg_value. If a particularly malformed provider response happens to emit four or more trailing > (e.g. </arg_value>>>>>), the regex will only strip </arg_value>>> and leave the remainder in the value.

Using >* (zero-or-more) instead of >{0,2} would handle any number of trailing > characters without changing behavior for the documented cases:

Suggested change
const XML_ARG_VALUE_SUFFIX_RE = /<\/arg_value>>{0,2}$/;
const XML_ARG_VALUE_SUFFIX_RE = /<\/arg_value>>*$/;
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-tools.params.ts
Line: 96

Comment:
**Regex quantifier may leave residual `>` for unusual provider output**

The pattern `>>{0,2}$` matches `</arg_value>` followed by 0, 1, or 2 additional `>` characters — handling 1, 2, or 3 total `>` after `arg_value`. If a particularly malformed provider response happens to emit four or more trailing `>` (e.g. `</arg_value>>>>>`), the regex will only strip `</arg_value>>>` and leave the remainder in the value.

Using `>*` (zero-or-more) instead of `>{0,2}` would handle any number of trailing `>` characters without changing behavior for the documented cases:

```suggestion
const XML_ARG_VALUE_SUFFIX_RE = /<\/arg_value>>*$/;
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +230 to +232
// Strip malformed XML closing-tag suffixes leaked by some providers (e.g. Qwen/DashScope).
// See https://github.com/openclaw/openclaw/issues/48780
params.command = stripXmlArgValueSuffix(params.command);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Other string exec params not sanitized

Only params.command is stripped, but the exec tool also accepts other string-typed parameters — workdir, host, security, ask, and node — that could be equally affected if the provider leaks the </arg_value> artifact into those values. A malformed workdir would silently break the working-directory resolution, which is hard to debug.

Consider sanitizing the full set of string exec params, or centralising via a shared helper similar to stripXmlArgValueSuffixFromRecord:

// Strip all string exec params, not just command
for (const key of ["command", "workdir", "host", "security", "ask", "node"] as const) {
  if (typeof params[key] === "string") {
    params[key] = stripXmlArgValueSuffix(params[key]!);
  }
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/bash-tools.exec.ts
Line: 230-232

Comment:
**Other string exec params not sanitized**

Only `params.command` is stripped, but the exec tool also accepts other string-typed parameters — `workdir`, `host`, `security`, `ask`, and `node` — that could be equally affected if the provider leaks the `</arg_value>` artifact into those values. A malformed `workdir` would silently break the working-directory resolution, which is hard to debug.

Consider sanitizing the full set of string exec params, or centralising via a shared helper similar to `stripXmlArgValueSuffixFromRecord`:

```typescript
// Strip all string exec params, not just command
for (const key of ["command", "workdir", "host", "security", "ask", "node"] as const) {
  if (typeof params[key] === "string") {
    params[key] = stripXmlArgValueSuffix(params[key]!);
  }
}
```

How can I resolve this? If you propose a fix, please make it concise.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 28, 2026

Codex review: needs changes before merge.

Summary
The PR adds </arg_value> XML suffix stripping for tool parameter normalization and exec commands, plus unit tests for the helper.

Reproducibility: yes. for source-level reproduction: the linked bug gives the Windows Qwen/DashScope corrupted exec and read examples, and current main forwards command/path strings unchanged. Live provider reproduction was not run during this read-only review.

Next step before merge
This is a narrow, valid bug with clear affected files and concrete review findings that an automated repair can address on a rebased source branch or focused replacement branch.

Security
Cleared: The diff only changes in-process tool argument sanitization and tests; it does not add dependency, CI, secret, package, or external code-execution surface changes.

Review findings

  • [P1] Move stripping to the active current-main boundary — src/agents/pi-tools.params.ts:140-142
  • [P1] Restrict XML stripping to command and path fields — src/agents/pi-tools.params.ts:140-142
  • [P2] Sanitize exec string options consistently — src/agents/bash-tools.exec.ts:230-232
Review details

Best possible solution:

Land a rebased, field-scoped sanitizer on current main that strips the malformed suffix only from command/path-like tool arguments, covers all exec string options, preserves write/edit payload text, and adds regression plus negative tests.

Do we have a high-confidence way to reproduce the issue?

Yes for source-level reproduction: the linked bug gives the Windows Qwen/DashScope corrupted exec and read examples, and current main forwards command/path strings unchanged. Live provider reproduction was not run during this read-only review.

Is this the best way to solve the issue?

No. The defensive direction is right, but this implementation targets a stale normalizer, mutates all string params including payload text, and only sanitizes exec command; the safer solution is a current-main field-scoped sanitizer.

Full review comments:

  • [P1] Move stripping to the active current-main boundary — src/agents/pi-tools.params.ts:140-142
    normalizeToolParams() is no longer the active read/write/edit boundary on current main; those tools validate and forward params through wrapToolParamValidation(). Rebase the fix onto the current wrappers, otherwise the PR will not address the active code path cleanly.
    Confidence: 0.9
  • [P1] Restrict XML stripping to command and path fields — src/agents/pi-tools.params.ts:140-142
    This record-wide call runs after content, oldText, and newText are normalized, so valid write/edit payload text ending with </arg_value> can be truncated before execution. Scope suffix stripping to affected command/path-like fields and add negative tests that preserve payload text.
    Confidence: 0.91
  • [P2] Sanitize exec string options consistently — src/agents/bash-tools.exec.ts:230-232
    The exec handler only strips command, but workdir, host, security, ask, and node are also string params consumed later. If the provider suffix lands in one of those values, exec can still fail or resolve the wrong option.
    Confidence: 0.8
  • [P3] Allow any trailing XML suffix chevrons — src/agents/pi-tools.params.ts:96
    The regex only matches up to three total > characters after arg_value, so a value ending with </arg_value>>>> leaves residual characters. Use an unbounded trailing-chevron suffix matcher for the malformed closing tag.
    Confidence: 0.84

Overall correctness: patch is incorrect
Overall confidence: 0.88

Acceptance criteria:

  • pnpm test src/agents/pi-tools.params.test.ts src/agents/pi-tools.create-openclaw-coding-tools.test.ts src/agents/bash-tools.exec.path.test.ts
  • pnpm exec oxfmt --check --threads=1 src/agents/pi-tools.params.ts src/agents/pi-tools.params.test.ts src/agents/pi-tools.read.ts src/agents/bash-tools.exec.ts CHANGELOG.md
  • pnpm check:changed in Testbox before handoff if runtime code changes

What I checked:

  • PR state: GitHub API reports this PR is open, not draft, head 71c6c921fd6de44125fb755380e4076ea12078e8, and currently unmergeable against its old base. (71c6c921fd6d)
  • No current sanitizer: Current main has no arg_value, stripXmlArgValueSuffix, or XML_ARG_VALUE implementation under src/agents. (89db1e5440f5)
  • Active param wrapper forwards raw params: wrapToolParamValidation() validates required params, then calls the wrapped tool with the original params object without a normalization or sanitization hook. (src/agents/pi-tools.params.ts:132, 89db1e5440f5)
  • Read/write/edit use current wrappers: Current write and edit tool factories wrap the active tools with wrapToolParamValidation(), not the PR's legacy normalizeToolParams() path. (src/agents/pi-tools.read.ts:641, 89db1e5440f5)
  • Current tests reject alias normalization: The current params test asserts that runtime validation does not normalize file_path, confirming the PR's normalizer-based tests do not match main's active contract. (src/agents/pi-tools.params.test.ts:28, 89db1e5440f5)
  • Exec consumes multiple raw string params: The exec handler accepts command, workdir, host, security, ask, and node, then consumes those strings later without suffix cleanup on current main. (src/agents/bash-tools.exec.ts:1240, 89db1e5440f5)

Likely related people:

  • steipete: Recent main history for src/agents/pi-tools.params.ts, src/agents/pi-tools.read.ts, and src/agents/bash-tools.exec.ts includes the current param-wrapper alignment and multiple exec refactors. (role: recent maintainer and major refactor owner; confidence: high; commits: ce1cd26fbcb0, d380ed710df1, 99176e1950e2; files: src/agents/pi-tools.params.ts, src/agents/pi-tools.read.ts, src/agents/bash-tools.exec.ts)
  • priyansh19: Authored the recent missing-parameter error behavior in pi-tools.params.ts, which is part of the same required-param wrapper boundary this fix must use. (role: adjacent validation contributor; confidence: medium; commits: 77e636cf7854; files: src/agents/pi-tools.params.ts)
  • vyctorbrzezowski: Authored recent exec host target validation work in the same exec string-option path affected by this sanitizer. (role: adjacent exec option contributor; confidence: medium; commits: df0074768c97; files: src/agents/bash-tools.exec.ts)

Remaining risk / open question:

  • The contributor branch is stale and currently unmergeable, so the repair may need a rebased source branch or a narrow replacement branch.
  • Live DashScope/Qwen reproduction was not run; the reproduction assessment is source-level against the reported malformed arguments and current main forwarding behavior.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 89db1e5440f5.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: [Windows] exec() and read() commands corrupted with </arg_value>> suffix

1 participant