[Bug] CotAgentOutputParser overwrites action_name with non-input keys and collapses multi-block code fences
Maintainers usually reply within 1 day
@lazerg is already working on this.
Since Nov 2, 2026.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 70/100
Research direction
Start in api/core/agent/output_parser/cot_output_parser.py, at the loop that assigns action_name in parse_action and at the regex in extra_json_from_code_block. Run the two reproduction snippets in the issue to confirm the overwritten action name and the fused code block. Done when the action name stays as the explicit action key even with extra keys like thought, and multiple fenced JSON blocks are parsed separately. The issue names no test file, so look for existing parser tests nearby.
Written by the indexing model from the issue text.
Description
Problem Description
In api/core/agent/output_parser/cot_output_parser.py:
1. Action Name Overwrite Defect (Lines 28-33)
for key, value in action.items():
if "input" in key.lower():
action_input = value
else:
action_name = value
When an LLM outputs an action dictionary containing supplementary keys like thought, reasoning, or tool_id:
{
"action": "google_search",
"action_input": {"query": "latest AI news"},
"thought": "I should search Google for the latest AI news"
}
Any non-input key hits the else: branch and blindly overwrites action_name. Here, action_name becomes "I should search Google..." instead of "google_search", causing subsequent tool invocation to fail with ToolNotFound.
2. Greedy Code Block Extraction Defect (Line 43)
blocks = re.findall(r"```[json]*\s*([\[{].*[]}])\s*```", code_block, re.DOTALL | re.IGNORECASE)
[json]*matches any permutation of charactersj, s, o, nrather than an optional(?:json)?language identifier..*withre.DOTALLis greedy. When an agent output contains multiple code blocks in a multi-step stream, the regex matches from the first{across closing code fences and text until the final}of the last block.json.loadsthrowsJSONDecodeError, and line 52 catches the exception and returns[], causing complete tool call dropping.
Reproduction Code
import re, json
# 1. Action Overwrite Repro
action = {
"action": "google_search",
"action_input": {"query": "AI news"},
"thought": "Let me search"
}
action_name = None
action_input = None
for key, value in action.items():
if "input" in key.lower():
action_input = value
else:
action_name = value
print("action_name:", action_name) # "Let me search" (Overwritten!)
# 2. Greedy Multi-Block Repro
code_text = """```json
{"action": "calc", "action_input": "2+2"}
Next:
{"action": "verify", "action_input": "4"}
```"""
blocks = re.findall(r"```[json]*\s*([\[{].*[]}])\s*```", code_text, re.DOTALL | re.IGNORECASE)
print("Blocks matched:", len(blocks)) # 1 fused block (invalid JSON)
Proposed Fix
- In
parse_action, prioritize explicit keys (action,tool,action_name) before general scanning. - In
extra_json_from_code_block, use non-greedy matching[\s\S]*?with(?:json)?:
def parse_action(action) -> str | AgentScratchpadUnit.Action:
if isinstance(action, str):
try:
action = json.loads(action, strict=False)
except json.JSONDecodeError:
return action or ""
if isinstance(action, list) and len(action) == 1:
action = action[0]
if not isinstance(action, dict):
return json.dumps(action)
action_name = None
action_input = None
for key in ["action", "tool", "tool_name", "action_name"]:
if key in action:
action_name = action[key]
break
for key in ["action_input", "tool_input", "parameters", "arguments", "input"]:
if key in action:
action_input = action[key]
break
if action_name is None or action_input is None:
for key, value in action.items():
if "input" in key.lower() and action_input is None:
action_input = value
elif any(k in key.lower() for k in ["action", "tool"]) and action_name is None:
action_name = value
if action_name is not None and action_input is not None:
return AgentScratchpadUnit.Action(
action_name=action_name,
action_input=action_input,
)
return json.dumps(action)
def extra_json_from_code_block(code_block) -> list[list | dict]:
blocks = re.findall(r"```(?:json)?\s*([\[{][\s\S]*?[\]}])\s*```", code_block, re.IGNORECASE)
if not blocks:
return []
json_blocks = []
for block in blocks:
try:
json_text = re.sub(r"^[a-zA-Z]+\n", "", block.strip(), flags=re.MULTILINE)
json_blocks.append(json.loads(json_text, strict=False))
except Exception:
continue
return json_blocks
Tested locally: preserves exact tool name across all action payloads and correctly parses multiple code blocks in streaming CoT pipelines.
- Dominant language
- Python
- Stars
- 160k
- Forks
- 25.4k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 608
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from langgenius/dify
-
Custom API tool sends unset optional header and cookie parameters as empty values or "None"Possibly taken @TemitopeKadri claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
langgenius/dify#46010 · 1 reaction ·
Maintainers usually reply within 1 day
-
Detail sidebar collapse drops keyboard focusPossibly taken @GYJ99 claimed this 1 day ago. Openproject#dify
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
langgenius/dify#45934 · 2 comments · 1 reaction ·
Maintainers usually reply within 1 day
-
[Chore/Refactor] bring jieba keyword table handler tests under strict pyreflyMay be free again @Luckyou-d claimed this 7 days ago, and no pull request is open. Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
langgenius/dify#45319 · 1 comment ·
Maintainers usually reply within 1 day
-
[Bug] Agent resource access page loses its sidebar on refreshPossibly taken @XiaoBa-Yu claimed this 8 days ago. Open1.18.0
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
langgenius/dify#45291 · 1 reaction ·
Maintainers usually reply within 1 day
-
Parent-child full-doc mode raises IndexError when extraction yields zero documentsPossibly taken @simpleqt claimed this 9 days ago. Open1.18.0
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
langgenius/dify#45272 · 1 comment · 1 reaction ·
Maintainers usually reply within 1 day
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
Maintainers usually reply within 1 day
-
With emit_ontology_keys, one ontology key change is reported as two errors (keys and keys_digest)Possibly taken @rvandewater claimed this today. Openbug priority:low
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 1/5 1-3 hours Newbie friendliness 84/100
iausathub/satchecker#286 ·
Maintainers usually reply within 1 day
-
bug test
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
OpenAssetIO/OpenAssetIO#1527 ·
-
automated endpoint-dead needs-curation
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
almanac-data/climate-almanac#128 ·
Maintainers usually reply within 1 day