Skip to content

Commit b3d0e4a

Browse files
committed
fix(session_formatter): render invalid tool error
1 parent d56458c commit b3d0e4a

File tree

4 files changed

+301
-2
lines changed

4 files changed

+301
-2
lines changed

lua/opencode/ui/session_formatter.lua

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -597,8 +597,12 @@ function M._format_tool(part)
597597
M._format_action(icons.get('tool') .. ' tool', tool)
598598
end
599599

600-
if part.state and part.state.status == 'error' then
601-
M._format_callout('ERROR', part.state.error)
600+
if part.state then
601+
if part.state.status == 'error' then
602+
M._format_callout('ERROR', part.state.error)
603+
elseif part.state.input and part.state.input.error then
604+
M._format_callout('ERROR', part.state.input.error)
605+
end
602606
end
603607

604608
if
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"timestamp":1760387420,"lines":["","---","","","** tool** `invalid`","> [!ERROR]",">","> Invalid input for tool edit: JSON parsing failed: Text: {\"filePath\": \"/Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/streaming_renderer.lua\", \"newString\": \"---Event handler for permission.replied events\\n---Re-renders part after permission is resolved\\n---@param event table Event object\\nfunctio.","> Error message: JSON Parse error: Unterminated string",""],"extmarks":[[1,2,0,{"priority":10,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":false,"ns_id":3,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-13 13:10:06)","OpencodeHint"],[" [msg_9df31cc90001HGn2UbFUgqJnLr]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3}],[7,4,0,{"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1}],[8,5,0,{"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1}],[9,6,0,{"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1}],[10,7,0,{"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1}],[11,8,0,{"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1}]]}

tests/data/tool-invalid.json

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
[
2+
{
3+
"type": "server.connected",
4+
"properties": {}
5+
},
6+
{
7+
"type": "session.updated",
8+
"properties": {
9+
"info": {
10+
"title": "removal",
11+
"directory": "/Users/cam/Dev/neovim-dev/opencode.nvim",
12+
"projectID": "29d43526f88157cd4edd071b899dd01f240b771b",
13+
"version": "0.15.0",
14+
"id": "ses_6210836a9ffejgCtBDIpNCsYMt",
15+
"time": {
16+
"created": 1760382404951,
17+
"updated": 1760382404951
18+
}
19+
}
20+
}
21+
},
22+
{
23+
"type": "message.updated",
24+
"properties": {
25+
"info": {
26+
"modelID": "claude-sonnet-4.5",
27+
"role": "assistant",
28+
"sessionID": "ses_6210836a9ffejgCtBDIpNCsYMt",
29+
"time": {
30+
"created": 1760386206864
31+
},
32+
"tokens": {
33+
"cache": {
34+
"read": 0,
35+
"write": 0
36+
},
37+
"reasoning": 0,
38+
"input": 0,
39+
"output": 0
40+
},
41+
"cost": 0,
42+
"path": {
43+
"cwd": "/Users/cam/Dev/neovim-dev/opencode.nvim",
44+
"root": "/Users/cam/Dev/neovim-dev/opencode.nvim"
45+
},
46+
"providerID": "github-copilot",
47+
"id": "msg_9df31cc90001HGn2UbFUgqJnLr",
48+
"mode": "build"
49+
}
50+
}
51+
},
52+
{
53+
"type": "message.part.updated",
54+
"properties": {
55+
"part": {
56+
"sessionID": "ses_6210836a9ffejgCtBDIpNCsYMt",
57+
"messageID": "msg_9df31cc90001HGn2UbFUgqJnLr",
58+
"id": "prt_9df31dbba0016WMz330tCy15xM",
59+
"type": "step-start"
60+
}
61+
}
62+
},
63+
{
64+
"type": "message.part.updated",
65+
"properties": {
66+
"part": {
67+
"callID": "toolu_01S6VFmzFVgdfCCYZDV3sokx",
68+
"tool": "edit",
69+
"state": {
70+
"status": "pending"
71+
},
72+
"sessionID": "ses_6210836a9ffejgCtBDIpNCsYMt",
73+
"messageID": "msg_9df31cc90001HGn2UbFUgqJnLr",
74+
"id": "prt_9df31dc040016Voad2DzwhYBOm",
75+
"type": "tool"
76+
}
77+
}
78+
},
79+
{
80+
"type": "message.part.updated",
81+
"properties": {
82+
"part": {
83+
"callID": "toolu_01S6VFmzFVgdfCCYZDV3sokx",
84+
"tool": "invalid",
85+
"state": {
86+
"status": "running",
87+
"input": {
88+
"tool": "edit",
89+
"error": "Invalid input for tool edit: JSON parsing failed: Text: {\"filePath\": \"/Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/streaming_renderer.lua\", \"newString\": \"---Event handler for permission.replied events\\n---Re-renders part after permission is resolved\\n---@param event table Event object\\nfunctio.\nError message: JSON Parse error: Unterminated string"
90+
},
91+
"time": {
92+
"start": 1760386212898
93+
}
94+
},
95+
"sessionID": "ses_6210836a9ffejgCtBDIpNCsYMt",
96+
"messageID": "msg_9df31cc90001HGn2UbFUgqJnLr",
97+
"id": "prt_9df31dc040016Voad2DzwhYBOm",
98+
"type": "tool"
99+
}
100+
}
101+
},
102+
{
103+
"type": "message.part.updated",
104+
"properties": {
105+
"part": {
106+
"callID": "toolu_01S6VFmzFVgdfCCYZDV3sokx",
107+
"tool": "invalid",
108+
"state": {
109+
"status": "completed",
110+
"title": "Invalid Tool",
111+
"input": {
112+
"tool": "edit",
113+
"error": "Invalid input for tool edit: JSON parsing failed: Text: {\"filePath\": \"/Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/streaming_renderer.lua\", \"newString\": \"---Event handler for permission.replied events\\n---Re-renders part after permission is resolved\\n---@param event table Event object\\nfunctio.\nError message: JSON Parse error: Unterminated string"
114+
},
115+
"output": "The arguments provided to the tool are invalid: Invalid input for tool edit: JSON parsing failed: Text: {\"filePath\": \"/Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/streaming_renderer.lua\", \"newString\": \"---Event handler for permission.replied events\\n---Re-renders part after permission is resolved\\n---@param event table Event object\\nfunctio.\nError message: JSON Parse error: Unterminated string",
116+
"metadata": {},
117+
"time": {
118+
"end": 1760386212900,
119+
"start": 1760386212898
120+
}
121+
},
122+
"sessionID": "ses_6210836a9ffejgCtBDIpNCsYMt",
123+
"messageID": "msg_9df31cc90001HGn2UbFUgqJnLr",
124+
"id": "prt_9df31dc040016Voad2DzwhYBOm",
125+
"type": "tool"
126+
}
127+
}
128+
},
129+
{
130+
"type": "message.part.updated",
131+
"properties": {
132+
"part": {
133+
"tokens": {
134+
"cache": {
135+
"read": 0,
136+
"write": 0
137+
},
138+
"reasoning": 0,
139+
"input": 0,
140+
"output": 0
141+
},
142+
"cost": 0,
143+
"sessionID": "ses_6210836a9ffejgCtBDIpNCsYMt",
144+
"messageID": "msg_9df31cc90001HGn2UbFUgqJnLr",
145+
"id": "prt_9df31e425001bp5r5xQi4zr3Ph",
146+
"type": "step-finish"
147+
}
148+
}
149+
},
150+
{
151+
"type": "message.updated",
152+
"properties": {
153+
"info": {
154+
"modelID": "claude-sonnet-4.5",
155+
"role": "assistant",
156+
"sessionID": "ses_6210836a9ffejgCtBDIpNCsYMt",
157+
"time": {
158+
"created": 1760386206864
159+
},
160+
"tokens": {
161+
"cache": {
162+
"read": 0,
163+
"write": 0
164+
},
165+
"reasoning": 0,
166+
"input": 0,
167+
"output": 0
168+
},
169+
"cost": 0,
170+
"path": {
171+
"cwd": "/Users/cam/Dev/neovim-dev/opencode.nvim",
172+
"root": "/Users/cam/Dev/neovim-dev/opencode.nvim"
173+
},
174+
"providerID": "github-copilot",
175+
"id": "msg_9df31cc90001HGn2UbFUgqJnLr",
176+
"mode": "build"
177+
}
178+
}
179+
},
180+
{
181+
"type": "message.updated",
182+
"properties": {
183+
"info": {
184+
"modelID": "claude-sonnet-4.5",
185+
"role": "assistant",
186+
"sessionID": "ses_6210836a9ffejgCtBDIpNCsYMt",
187+
"time": {
188+
"created": 1760386206864,
189+
"completed": 1760386212950
190+
},
191+
"tokens": {
192+
"cache": {
193+
"read": 0,
194+
"write": 0
195+
},
196+
"reasoning": 0,
197+
"input": 0,
198+
"output": 0
199+
},
200+
"cost": 0,
201+
"path": {
202+
"cwd": "/Users/cam/Dev/neovim-dev/opencode.nvim",
203+
"root": "/Users/cam/Dev/neovim-dev/opencode.nvim"
204+
},
205+
"providerID": "github-copilot",
206+
"id": "msg_9df31cc90001HGn2UbFUgqJnLr",
207+
"mode": "build"
208+
}
209+
}
210+
},
211+
{
212+
"type": "message.updated",
213+
"properties": {
214+
"info": {
215+
"modelID": "claude-sonnet-4.5",
216+
"role": "assistant",
217+
"sessionID": "ses_6210836a9ffejgCtBDIpNCsYMt",
218+
"time": {
219+
"created": 1760386206864,
220+
"completed": 1760386212951
221+
},
222+
"tokens": {
223+
"cache": {
224+
"read": 0,
225+
"write": 0
226+
},
227+
"reasoning": 0,
228+
"input": 0,
229+
"output": 0
230+
},
231+
"cost": 0,
232+
"path": {
233+
"cwd": "/Users/cam/Dev/neovim-dev/opencode.nvim",
234+
"root": "/Users/cam/Dev/neovim-dev/opencode.nvim"
235+
},
236+
"providerID": "github-copilot",
237+
"id": "msg_9df31cc90001HGn2UbFUgqJnLr",
238+
"mode": "build"
239+
}
240+
}
241+
},
242+
{
243+
"type": "message.updated",
244+
"properties": {
245+
"info": {
246+
"modelID": "claude-sonnet-4.5",
247+
"role": "assistant",
248+
"sessionID": "ses_6210836a9ffejgCtBDIpNCsYMt",
249+
"time": {
250+
"created": 1760386206864,
251+
"completed": 1760386212951
252+
},
253+
"tokens": {
254+
"cache": {
255+
"read": 0,
256+
"write": 0
257+
},
258+
"reasoning": 0,
259+
"input": 0,
260+
"output": 0
261+
},
262+
"cost": 0,
263+
"path": {
264+
"cwd": "/Users/cam/Dev/neovim-dev/opencode.nvim",
265+
"root": "/Users/cam/Dev/neovim-dev/opencode.nvim"
266+
},
267+
"providerID": "github-copilot",
268+
"id": "msg_9df31cc90001HGn2UbFUgqJnLr",
269+
"mode": "build"
270+
}
271+
}
272+
},
273+
{
274+
"type": "session.idle",
275+
"properties": {
276+
"sessionID": "ses_6210836a9ffejgCtBDIpNCsYMt"
277+
}
278+
}
279+
]

tests/unit/streaming_renderer_spec.lua

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,19 @@ describe('streaming_renderer', function()
130130
assert.same(expected.lines, actual.lines)
131131
assert.same(expected.extmarks, helpers.normalize_namespace_ids(actual.extmarks))
132132
end)
133+
134+
it('replays tool-invalid correctly', function()
135+
local events = helpers.load_test_data('tests/data/tool-invalid.json')
136+
state.active_session = helpers.get_session_from_events(events)
137+
local expected = helpers.load_test_data('tests/data/tool-invalid.expected.json')
138+
139+
helpers.replay_events(events)
140+
141+
vim.wait(200)
142+
143+
local actual = helpers.capture_output(state.windows.output_buf, streaming_renderer._namespace)
144+
145+
assert.same(expected.lines, actual.lines)
146+
assert.same(expected.extmarks, helpers.normalize_namespace_ids(actual.extmarks))
147+
end)
133148
end)

0 commit comments

Comments
 (0)