github/github-mcp-server · error

failed to marshal workflow: %w

Error message

failed to marshal workflow: %w

What it means

Defensive guard in getWorkflow (pkg/github/actions.go:793): json.Marshal of a *github.Workflow returned by GetWorkflowByID or GetWorkflowByFileName failed. go-github structs are plain JSON-safe data types, so this is effectively unreachable unless the struct contains a non-serializable field (func, chan, NaN, cycle) - which would be a go-github or server regression, not caller input.

Source

Thrown at pkg/github/actions.go:793

func getWorkflow(ctx context.Context, client *github.Client, owner, repo, resourceID string) (*mcp.CallToolResult, any, error) {
	var workflow *github.Workflow
	var resp *github.Response
	var err error

	if workflowIDInt, parseErr := strconv.ParseInt(resourceID, 10, 64); parseErr == nil {
		workflow, resp, err = client.Actions.GetWorkflowByID(ctx, owner, repo, workflowIDInt)
	} else {
		workflow, resp, err = client.Actions.GetWorkflowByFileName(ctx, owner, repo, resourceID)
	}

	if err != nil {
		return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to get workflow", resp, err), nil, nil
	}

	defer func() { _ = resp.Body.Close() }()
	r, err := json.Marshal(workflow)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to marshal workflow: %w", err)
	}

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

func getWorkflowRun(ctx context.Context, client *github.Client, owner, repo string, resourceID int64) (*mcp.CallToolResult, any, error) {
	workflowRun, resp, err := client.Actions.GetWorkflowRunByID(ctx, owner, repo, resourceID)
	if err != nil {
		return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to get workflow run", resp, err), nil, nil
	}
	defer func() { _ = resp.Body.Close() }()
	r, err := json.Marshal(convertToMinimalWorkflowRun(workflowRun))
	if err != nil {
		return nil, nil, fmt.Errorf("failed to marshal workflow run: %w", err)
	}
	return utils.NewToolResultText(string(r)), nil, nil
}

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. Report upstream with the exact arguments and versions - input cannot trigger it
  2. Pin go-github and github-mcp-server to known-good versions
  3. If patching, marshal a minimal DTO instead of the raw struct
  4. Add a regression test marshaling the struct shape

Example fix

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

// after - marshal a stable DTO to decouple from go-github struct churn
minimal := map[string]any{
    "id": workflow.GetID(), "name": workflow.GetName(), "path": workflow.GetPath(), "state": workflow.GetState(),
}
r, err := json.Marshal(minimal)
Defensive patterns

Strategy: validation

Validate before calling

// Maintainer-side: keep the marshaled payload JSON-safe
if _, err := json.Marshal(workflow); err != nil {
    return fmt.Errorf("workflow not JSON-safe before tool return: %w", err)
}

Try / catch

if err != nil {
    // deterministic serialization failure: report, never retry
    reportIssue("get_action_workflow", args, err, version())
    return err
}

Prevention

When it happens

Trigger: Calling get_action_workflow (by numeric ID or by .yml file name) after a go-github upgrade that introduced a JSON-unsafe field; fork modifications embedding non-serializable state in the workflow struct.

Common situations: Virtually never in stock deployments; appears in forks or after unusual dependency versions.

Related errors


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