wavetermdev/waveterm · error

failed to marshal input: %w

Error message

failed to marshal input: %w

What it means

parseTermGetScrollbackInput normalizes the tool's loosely-typed input (any) by round-tripping it through JSON: marshal to bytes, then unmarshal into TermGetScrollbackToolInput. This error wraps a failure of the json.Marshal step. In practice this is rare because the marshal target is the caller-provided value, but non-serializable values (channels, funcs, cyclic maps) or invalid values would trigger it.

Source

Thrown at pkg/aiusechat/tools_term.go:64

func parseTermGetScrollbackInput(input any) (*TermGetScrollbackToolInput, error) {
	const (
		DefaultCount = 200
		MaxCount     = 1000
	)

	result := &TermGetScrollbackToolInput{
		LineStart: 0,
		Count:     0,
	}

	if input == nil {
		result.Count = DefaultCount
		return result, nil
	}

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

	if err := json.Unmarshal(inputBytes, result); err != nil {
		return nil, fmt.Errorf("failed to unmarshal input: %w", err)
	}

	if result.Count == 0 {
		result.Count = DefaultCount
	}

	if result.Count < 0 {
		return nil, fmt.Errorf("count must be positive")
	}

	result.Count = min(result.Count, MaxCount)

	return result, nil
}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Inspect the wrapped error to identify the unsupported type (json.UnsupportedTypeError) or cycle (json.UnsupportedValueError)
  2. Ensure tool input is plain JSON-compatible data (strings, numbers, bools, maps, slices) before invoking the parser
  3. Build input from JSON-decoded arguments (map[string]any) as the tool framework does
  4. If constructing input in tests/harnesses, only use marshalable values

Example fix

// before
input := map[string]any{"widget_id": "b1", "count": someFunc} // not marshalable
parsed, err := parseTermGetScrollbackInput(input)
// after
input := map[string]any{"widget_id": "b1", "count": 100} // JSON-compatible
parsed, err := parseTermGetScrollbackInput(input)
Defensive patterns

Strategy: validation

Validate before calling

func isJSONMarshallable(v any) error {
    _, err := json.Marshal(v)
    return err
}
if err := isJSONMarshallable(input); err != nil { return nil, err }

Type guard

func isPlainJSONValue(v any) bool {
    switch v.(type) {
    case nil, bool, string, int, int64, float64, json.Number:
        return true
    case map[string]any:
        for _, e := range v.(map[string]any) { if !isPlainJSONValue(e) { return false } }
        return true
    case []any:
        for _, e := range v.([]any) { if !isPlainJSONValue(e) { return false } }
        return true
    default:
        return false
    }
}

Prevention

When it happens

Trigger: Calling term_get_scrollback's parse path with an input value that encoding/json cannot marshal — e.g. a map containing a channel, function, or cyclic reference instead of plain JSON-compatible data.

Common situations: Programmatic/preview invocations of the tool callback passing raw Go values rather than JSON-decoded tool arguments; custom tool harnesses injecting unsupported types; unsupported-type json.UnsupportedTypeError from a misbuilt input map.

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 wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/6f0392d328c211b6. Report an issue: GitHub.