fix(look-at): handle JSON parse errors from session.prompt gracefully (#1216)

When multimodal-looker agent returns empty/malformed response, the SDK
throws 'JSON Parse error: Unexpected EOF'. This commit adds try-catch
around session.prompt() to provide user-friendly error message with
troubleshooting guidance.

- Add error handling for JSON parse errors with detailed guidance
- Add error handling for generic prompt failures
- Add test cases for both error scenarios
This commit is contained in:
YeonGyu-Kim
2026-01-28 23:58:01 +09:00
committed by GitHub
parent 9d3e152b19
commit 3ab4529bc7
2 changed files with 119 additions and 16 deletions

View File

@@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test"
import { normalizeArgs, validateArgs } from "./tools"
import { normalizeArgs, validateArgs, createLookAt } from "./tools"
describe("look-at tool", () => {
describe("normalizeArgs", () => {
@@ -70,4 +70,80 @@ describe("look-at tool", () => {
expect(error).toContain("file_path")
})
})
describe("createLookAt error handling", () => {
// #given session.prompt에서 JSON parse 에러 발생
// #when LookAt 도구 실행
// #then 사용자 친화적 에러 메시지 반환
test("handles JSON parse error from session.prompt gracefully", async () => {
const mockClient = {
session: {
get: async () => ({ data: { directory: "/project" } }),
create: async () => ({ data: { id: "ses_test_json_error" } }),
prompt: async () => {
throw new Error("JSON Parse error: Unexpected EOF")
},
messages: async () => ({ data: [] }),
},
}
const tool = createLookAt({
client: mockClient,
directory: "/project",
} as any)
const toolContext = {
sessionID: "parent-session",
messageID: "parent-message",
agent: "sisyphus",
abort: new AbortController().signal,
}
const result = await tool.execute(
{ file_path: "/test/file.png", goal: "analyze image" },
toolContext
)
expect(result).toContain("Error: Failed to analyze file")
expect(result).toContain("malformed response")
expect(result).toContain("multimodal-looker")
expect(result).toContain("image/png")
})
// #given session.prompt에서 일반 에러 발생
// #when LookAt 도구 실행
// #then 원본 에러 메시지 포함한 에러 반환
test("handles generic prompt error gracefully", async () => {
const mockClient = {
session: {
get: async () => ({ data: { directory: "/project" } }),
create: async () => ({ data: { id: "ses_test_generic_error" } }),
prompt: async () => {
throw new Error("Network connection failed")
},
messages: async () => ({ data: [] }),
},
}
const tool = createLookAt({
client: mockClient,
directory: "/project",
} as any)
const toolContext = {
sessionID: "parent-session",
messageID: "parent-message",
agent: "sisyphus",
abort: new AbortController().signal,
}
const result = await tool.execute(
{ file_path: "/test/file.pdf", goal: "extract text" },
toolContext
)
expect(result).toContain("Error: Failed to send prompt")
expect(result).toContain("Network connection failed")
})
})
})

View File

@@ -131,22 +131,49 @@ Original error: ${createResult.error}`
log(`[look_at] Created session: ${sessionID}`)
log(`[look_at] Sending prompt with file passthrough to session ${sessionID}`)
await ctx.client.session.prompt({
path: { id: sessionID },
body: {
agent: MULTIMODAL_LOOKER_AGENT,
tools: {
task: false,
call_omo_agent: false,
look_at: false,
read: false,
try {
await ctx.client.session.prompt({
path: { id: sessionID },
body: {
agent: MULTIMODAL_LOOKER_AGENT,
tools: {
task: false,
call_omo_agent: false,
look_at: false,
read: false,
},
parts: [
{ type: "text", text: prompt },
{ type: "file", mime: mimeType, url: pathToFileURL(args.file_path).href, filename },
],
},
parts: [
{ type: "text", text: prompt },
{ type: "file", mime: mimeType, url: pathToFileURL(args.file_path).href, filename },
],
},
})
})
} catch (promptError) {
const errorMessage = promptError instanceof Error ? promptError.message : String(promptError)
log(`[look_at] Prompt error:`, promptError)
const isJsonParseError = errorMessage.includes("JSON") && (errorMessage.includes("EOF") || errorMessage.includes("parse"))
if (isJsonParseError) {
return `Error: Failed to analyze file - received malformed response from multimodal-looker agent.
This typically occurs when:
1. The multimodal-looker model is not available or not connected
2. The model does not support this file type (${mimeType})
3. The API returned an empty or truncated response
File: ${args.file_path}
MIME type: ${mimeType}
Try:
- Ensure a vision-capable model (e.g., gemini-3-flash, gpt-5.2) is available
- Check provider connections in opencode settings
- For text files like .md, .txt, use the Read tool instead
Original error: ${errorMessage}`
}
return `Error: Failed to send prompt to multimodal-looker agent: ${errorMessage}`
}
log(`[look_at] Prompt sent, fetching messages...`)