{"record":{"id":"06819dee69bb856d","repo":"github/copilot-sdk","slug":"failed-to-serialize-result-w","errorCode":null,"errorMessage":"failed to serialize result: %w","messagePattern":"failed to serialize result: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/definetool.go","lineNumber":95,"sourceCode":"\t}\n\n\t// ToolResult passes through directly\n\tif tr, ok := result.(ToolResult); ok {\n\t\treturn tr, nil\n\t}\n\n\t// Strings pass through directly\n\tif str, ok := result.(string); ok {\n\t\treturn ToolResult{\n\t\t\tTextResultForLLM: str,\n\t\t\tResultType:       \"success\",\n\t\t}, nil\n\t}\n\n\t// Everything else gets JSON-serialized\n\tjsonBytes, err := json.Marshal(result)\n\tif err != nil {\n\t\treturn ToolResult{}, fmt.Errorf(\"failed to serialize result: %w\", err)\n\t}\n\n\treturn ToolResult{\n\t\tTextResultForLLM: string(jsonBytes),\n\t\tResultType:       \"success\",\n\t}, nil\n}\n\n// ConvertMCPCallToolResult converts an MCP CallToolResult value (a map or struct\n// with a \"content\" array and optional \"isError\" bool) into a ToolResult.\n// Returns the converted ToolResult and true if the value matched the expected\n// shape, or a zero ToolResult and false otherwise.\nfunc ConvertMCPCallToolResult(value any) (ToolResult, bool) {\n\tm, ok := value.(map[string]any)\n\tif !ok {\n\t\tjsonBytes, err := json.Marshal(value)\n\t\tif err != nil {\n\t\t\treturn ToolResult{}, false","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/go/definetool.go#L77-L113","documentation":"normalizeResult converts a tool handler's return value into a ToolResult. Results that are not already string/structured get JSON-serialized; if json.Marshal on the result fails, this error is returned. It indicates the handler returned a value that cannot be represented as JSON.","triggerScenarios":"A tool handler returns a value containing non-JSON-serializable data (channels, funcs, cycles, NaN/Inf floats) that reaches the default JSON-serialization branch of normalizeResult.","commonSituations":"Handler accidentally returning a Go struct with a circular reference or func field; returning raw runtime values (e.g. a *os.File, error value with cycles) instead of a plain data structure.","solutions":["Return JSON-safe data (plain maps/structs with exported, serializable fields) from the handler","Tag or drop non-serializable struct fields (json:\"-\")","Pre-serialize problematic fields yourself (e.g. fmt.Sprint for display) before returning","If the wrapped error cites an unsupported type, locate and fix that field in the returned value"],"exampleFix":"// before\nreturn map[string]any{\"file\": os.Stdin}, nil // not serializable\n// after\nreturn map[string]any{\"file\": \"os.Stdin (descriptor)\"}, nil","handlingStrategy":"validation","validationCode":"// pre-flight in tests: ensure handler results marshal cleanly\nb, err := json.Marshal(handlerResult); if err != nil { t.Fatal(err) }","typeGuard":"// Go: reject non-serializable results before returning them\nfunc serializable(v any) error {\n\t_, err := json.Marshal(v)\n\treturn err\n}","tryCatchPattern":"// Go\nres, err := tool.Invoke(inv)\nif err != nil && strings.Contains(err.Error(), \"failed to serialize result\") {\n\t// handler returned non-JSON value; inspect and fix handler return\n}","preventionTips":["Return plain maps/structs with exported fields from handlers","Tag non-serializable fields with json:\"-\"","Avoid returning runtime handles (files, conns) as results"],"tags":["json","mcp","serialization"],"backgroundTag":"json-serialization-failed","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}