{"record":{"id":"0c7c376be1034549","repo":"github/github-mcp-server","slug":"failed-to-marshal-discussion-w","errorCode":null,"errorMessage":"failed to marshal discussion: %w","messagePattern":"failed to marshal discussion: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/github/discussions.go","lineNumber":381,"sourceCode":"\t\t\t\t\"title\":      string(d.Title),\n\t\t\t\t\"body\":       string(d.Body),\n\t\t\t\t\"url\":        string(d.URL),\n\t\t\t\t\"closed\":     bool(d.Closed),\n\t\t\t\t\"isAnswered\": bool(d.IsAnswered),\n\t\t\t\t\"createdAt\":  d.CreatedAt.Time,\n\t\t\t\t\"category\": map[string]any{\n\t\t\t\t\t\"name\": string(d.Category.Name),\n\t\t\t\t},\n\t\t\t}\n\n\t\t\t// Add optional timestamp fields if present\n\t\t\tif d.AnswerChosenAt != nil {\n\t\t\t\tresponse[\"answerChosenAt\"] = d.AnswerChosenAt.Time\n\t\t\t}\n\n\t\t\tout, err := json.Marshal(response)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"failed to marshal discussion: %w\", err)\n\t\t\t}\n\n\t\t\tresult := utils.NewToolResultText(string(out))\n\t\t\t// Discussion content is user-authored (untrusted); confidentiality\n\t\t\t// follows repo visibility.\n\t\t\tresult = attachRepoVisibilityIFCLabelLazy(ctx, deps, params.Owner, params.Repo, result, ifc.LabelRepoUserContent)\n\t\t\treturn result, nil, nil\n\t\t},\n\t)\n}\n\nfunc GetDiscussionComments(t translations.TranslationHelperFunc) inventory.ServerTool {\n\treturn NewTool(\n\t\tToolsetMetadataDiscussions,\n\t\tmcp.Tool{\n\t\t\tName:        \"get_discussion_comments\",\n\t\t\tDescription: t(\"TOOL_GET_DISCUSSION_COMMENTS_DESCRIPTION\", \"Get comments from a discussion\"),\n\t\t\tAnnotations: &mcp.ToolAnnotations{","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/pkg/github/discussions.go#L363-L399","documentation":"get_discussion marshals a map of primitives (id, title, URLs, category name, optional answerChosenAt time.Time) after a successful GraphQL query. The only realistically marshalable-risk value is the time.Time from d.AnswerChosenAt.Time, whose default encoding never fails; everything else is strings. json.Marshal errors require unsupported types or cycles, so this is a defensive guard that current data shapes cannot trip.","triggerScenarios":"Introduction of a non-marshalable field (channel, func, NaN float) into the response map in a refactor; a custom type with a broken MarshalJSON replacing string(d.Category.Name); cyclic data if discussion maps ever embed each other. No GitHub response triggers it today.","commonSituations":"Forks extending the response with typed objects; copy-paste of GraphQL struct fields (which carry extra methods) into the map instead of converting to strings; regressions after go-github/githubv4 upgrades that change field types.","solutions":["Verify every value in the response map is string/int/bool or time.Time, converting GraphQL scalar types with string(...)/fmt.Sprintf as the current code does","Add a golden JSON test for the get_discussion response shape","If it fires, it is a bug in this handler's construction, not in input data; check recent diffs to the response map","Return a degraded minimal response (id + url) rather than failing the whole tool if a marshaling regression is suspected"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"out, err := json.Marshal(response)\nif err != nil {\n\treturn nil, nil, fmt.Errorf(\"failed to marshal discussion: %w\", err)\n}","preventionTips":["Project only primitives and time.Time into response maps","Convert typed GraphQL fields with string(...)/fmt.Sprintf before marshaling","Golden-test the response shape including optional fields (answerChosenAt)"],"tags":["go","json-marshal","defensive-code","discussions"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}