wavetermdev/waveterm · error

failed to marshal input

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.

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.

Appendix: 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)