wavetermdev/waveterm · error

failed to adapt type from %T => %T, input type failed to mar

Error message

failed to adapt type from %T => %T, input type failed to marshal: %w

What it means

AtomImpl.SetVal adapts an arbitrary value to the atom's generic type T by JSON round-tripping (Marshal then Unmarshal). If the input value itself cannot be marshaled to JSON (channels, funcs, cyclic structures, unsupported types), setVal_nolock returns this error before any unmarshal is attempted.

Source

Thrown at tsunami/engine/atomimpl.go:62

func (a *AtomImpl[T]) setVal_nolock(val any) error {
	if val == nil {
		var zero T
		a.val = zero
		return nil
	}

	// Try direct assignment if it's already type T
	if typed, ok := val.(T); ok {
		a.val = typed
		return nil
	}

	// Try JSON marshaling/unmarshaling
	jsonBytes, err := json.Marshal(val)
	if err != nil {
		var result T
		return fmt.Errorf("failed to adapt type from %T => %T, input type failed to marshal: %w", val, result, err)
	}

	var result T
	if err := json.Unmarshal(jsonBytes, &result); err != nil {
		return fmt.Errorf("failed to adapt type from %T => %T: %w", val, result, err)
	}

	a.val = result
	return nil
}

func (a *AtomImpl[T]) SetVal(val any) error {
	a.lock.Lock()
	defer a.lock.Unlock()
	return a.setVal_nolock(val)
}

func (a *AtomImpl[T]) SetUsedBy(waveId string, used bool) {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Pass only JSON-serializable values to SetVal (strip funcs/channels into separate fields)
  2. Add `json:"-"` tags to non-serializable struct fields
  3. Pre-serialize to a plain type (map[string]any or a DTO struct) before SetVal
  4. Consider matching the atom's type T exactly so no adaptation is needed

Example fix

// before
atom.SetVal(Config{Callback: myFunc}) // funcs not marshalable
// after
atom.SetVal(Config{Name: cfg.Name}) // JSON-safe fields only
Defensive patterns

Strategy: validation

Validate before calling

func jsonSafe(v any) error {
    _, err := json.Marshal(v)
    return err
}
if err := jsonSafe(val); err != nil {
    return fmt.Errorf("value not settable on atom: %w", err)
}
atom.SetVal(val)

Type guard

func isJSONSerializable(v any) bool {
    switch v.(type) {
    case chan struct{}, func(), complex128:
        return false
    }
    return jsonSafe(v) == nil
}

Try / catch

if err := atom.SetVal(val); err != nil {
    if strings.Contains(err.Error(), "failed to marshal") {
        log.Printf("value %T is not JSON-serializable", val)
    }
}

Prevention

When it happens

Trigger: Calling atom.SetVal with a value containing a func, channel, complex number, or a struct with cyclic references — json.Marshal fails immediately.

Common situations: Passing a struct with func/callback fields into an atom typed as a plain data struct; storing a map containing non-serializable values; accidentally passing a channel or io.Writer.

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/c3e3f1e5457f48a6. Report an issue: GitHub.