github/copilot-sdk · error

failed to marshal arguments

Error message

failed to marshal arguments: %w

What it means

The typed tool wrapper converts the JSON-RPC invocation's Arguments (map[string]any) into the tool's typed params struct via a JSON round-trip. If json.Marshal on the arguments map fails, this error is returned. In practice this is rare because the arguments already came from JSON parsing; it guards against values that cannot be serialized (e.g. channels, funcs, or NaN injected programmatically).

Solutions

  1. Inspect the Arguments map at the call site for non-JSON-serializable values
  2. Construct invocations only with JSON-safe types (string, number, bool, nil, slices, maps)
  3. Validate/sanitize arguments before invoking the tool
  4. The wrapped err names the unsupported type — fix that field specifically

Example fix

// before
inv.Arguments["callback"] = func() {} // not JSON-serializable
// after
// only pass JSON-representable values:
inv.Arguments["mode"] = "sync"
Defensive patterns

Strategy: validation

Validate before calling

// ensure all argument values are JSON-serializable before invoking
json.Marshal(inv.Arguments) // do a pre-flight marshal in tests

Type guard

// Go: reject values encoding/json cannot serialize
func jsonSafe(v any) bool {
	b, err := json.Marshal(v)
	return err == nil && b != nil
}

Try / catch

// Go
res, err := tool.Invoke(inv)
if err != nil && strings.Contains(err.Error(), "failed to marshal arguments") {
	// fix the invocation's Arguments to contain only JSON-safe values
}

Prevention

When it happens

Trigger: An invocation whose Arguments map contains values unsupported by encoding/json (func, channel, complex types) — typically only possible when a caller constructs the invocation in Go code rather than receiving it over JSON-RPC.

Common situations: Unit tests or in-process tool invocation passing non-JSON-serializable argument values; programmatic wiring of a tool handler with malformed Arguments.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/0fcc1e004c6ef0cd. Report an issue: GitHub.

Appendix: source

Thrown at go/definetool.go:52

	return Tool{
		Name:        name,
		Description: description,
		Parameters:  schema,
		Handler:     createTypedHandler(handler),
	}
}

// createTypedHandler wraps a typed handler function into the standard ToolHandler signature.
func createTypedHandler[T any, U any](handler func(T, ToolInvocation) (U, error)) ToolHandler {
	return func(inv ToolInvocation) (ToolResult, error) {
		var params T

		// Convert arguments to typed struct via JSON round-trip
		// Arguments is already map[string]any from JSON-RPC parsing
		jsonBytes, err := json.Marshal(inv.Arguments)
		if err != nil {
			return ToolResult{}, fmt.Errorf("failed to marshal arguments: %w", err)
		}

		if err := json.Unmarshal(jsonBytes, &params); err != nil {
			return ToolResult{}, fmt.Errorf("failed to unmarshal arguments into %T: %w", params, err)
		}

		result, err := handler(params, inv)
		if err != nil {
			return ToolResult{}, err
		}

		return normalizeResult(result)
	}
}

// normalizeResult converts any value to a ToolResult.
// Strings pass through directly, ToolResult passes through, and other types
// are JSON-serialized.

View on GitHub (pinned to cd8cf15dc3)