github/github-mcp-server · error

failed to marshal response: %w

Error message

failed to marshal response: %w

What it means

Defensive guard in handleFailedJobsLogs (pkg/github/actions.go:108): json.Marshal failed on the assembled result map (message, run_id, total_jobs, failed_jobs, logs, return_format). Every value in that map is a string, int, bool, or nested map produced by the server itself, so with well-formed behavior this branch is effectively unreachable; it fires only if a code path injects a non-serializable Go value (func, chan, NaN float, cyclic pointer).

Source

Thrown at pkg/github/actions.go:108

			// Enable reporting of status codes and error causes
			_, _ = ghErrors.NewGitHubAPIErrorToCtx(ctx, "failed to get job logs", resp, err) // Explicitly ignore error for graceful handling
		}

		logResults = append(logResults, jobResult)
	}

	result := map[string]any{
		"message":       fmt.Sprintf("Retrieved logs for %d failed jobs", len(failedJobs)),
		"run_id":        runID,
		"total_jobs":    len(jobs.Jobs),
		"failed_jobs":   len(failedJobs),
		"logs":          logResults,
		"return_format": map[string]bool{"content": returnContent, "urls": !returnContent},
	}

	r, err := json.Marshal(result)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to marshal response: %w", err)
	}

	return utils.NewToolResultText(string(r)), nil, nil
}

// handleSingleJobLogs gets logs for a single job
func handleSingleJobLogs(ctx context.Context, client *github.Client, owner, repo string, jobID int64, returnContent bool, tailLines int, contentWindowSize int) (*mcp.CallToolResult, any, error) {
	jobResult, resp, err := getJobLogData(ctx, client, owner, repo, jobID, "", returnContent, tailLines, contentWindowSize)
	if err != nil {
		return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to get job logs", resp, err), nil, nil
	}

	r, err := json.Marshal(jobResult)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to marshal response: %w", err)
	}

	return utils.NewToolResultText(string(r)), nil, nil

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. No input change fixes it - it is a server-side bug: capture the exact tool arguments and open an issue against github-mcp-server
  2. If you maintain the code, keep only JSON-safe primitives (string/int/bool/nested maps) in result maps
  3. Test custom additions with json.Marshal before returning them
  4. Downgrade or upgrade to the nearest release where the tool worked

Example fix

// before
r, err := json.Marshal(result)
if err != nil {
    return nil, nil, fmt.Errorf("failed to marshal response: %w", err)
}

// after - keep maps JSON-safe and fail loudly in tests
r, err := json.Marshal(result)
if err != nil {
    return nil, nil, fmt.Errorf("failed to marshal response (result keys %v): %w", reflect.ValueOf(result).MapKeys(), err)
}
Defensive patterns

Strategy: validation

Validate before calling

// If you build logResults yourself, assert JSON safety before the tool returns
if _, err := json.Marshal(logResults); err != nil {
    return fmt.Errorf("non-serializable log result: %w", err)
}

Try / catch

if err != nil {
    // marshal of server-built data failed: not input-related, surface for triage
    return fmt.Errorf("tool %s marshal failure (server bug?): %w", toolName, err)
}

Prevention

When it happens

Trigger: Calling get_action_job_logs / failed-jobs log flows when a regression puts a non-JSON-serializable value into logResults or the result map; JSON cycle detection ("json: unsupported value: encountered a cycle via ...") on self-referential structures; math.NaN() floats in numeric fields.

Common situations: Almost never seen in production; appears after upgrading to a version with a serialization regression, or in forks that add custom fields (e.g. embedding *bytes.Buffer or a func) to the result map.

Related errors


AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15). Data as JSON: /api/errors/a839a78229f4063f. Report an issue: GitHub.