github/github-mcp-server · warning
failed to marshal discussion: %w
Error message
failed to marshal discussion: %w
What it means
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.
Source
Thrown at pkg/github/discussions.go:381
"title": string(d.Title),
"body": string(d.Body),
"url": string(d.URL),
"closed": bool(d.Closed),
"isAnswered": bool(d.IsAnswered),
"createdAt": d.CreatedAt.Time,
"category": map[string]any{
"name": string(d.Category.Name),
},
}
// Add optional timestamp fields if present
if d.AnswerChosenAt != nil {
response["answerChosenAt"] = d.AnswerChosenAt.Time
}
out, err := json.Marshal(response)
if err != nil {
return nil, nil, fmt.Errorf("failed to marshal discussion: %w", err)
}
result := utils.NewToolResultText(string(out))
// Discussion content is user-authored (untrusted); confidentiality
// follows repo visibility.
result = attachRepoVisibilityIFCLabelLazy(ctx, deps, params.Owner, params.Repo, result, ifc.LabelRepoUserContent)
return result, nil, nil
},
)
}
func GetDiscussionComments(t translations.TranslationHelperFunc) inventory.ServerTool {
return NewTool(
ToolsetMetadataDiscussions,
mcp.Tool{
Name: "get_discussion_comments",
Description: t("TOOL_GET_DISCUSSION_COMMENTS_DESCRIPTION", "Get comments from a discussion"),
Annotations: &mcp.ToolAnnotations{View on GitHub (pinned to 0ea1f775a7)
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
Defensive patterns
Strategy: try-catch
Try / catch
out, err := json.Marshal(response)
if err != nil {
return nil, nil, fmt.Errorf("failed to marshal discussion: %w", err)
} Prevention
- 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)
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to marshal discussions: %w
- failed to marshal comments: %w
- failed to marshal comment: %w
- failed to marshal discussion categories: %w
- failed to marshal response: %w
AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15).
Data as JSON: /api/errors/0c7c376be1034549.
Report an issue: GitHub.