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 <[email protected]>
@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.

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