wavetermdev/waveterm · error

failed to adapt type from %T => %T: %w

Error message

failed to adapt type from %T => %T: %w

What it means

Second half of the JSON round-trip in AtomImpl.SetVal: json.Marshal succeeded but json.Unmarshal into the target type T failed, meaning the value's JSON shape does not fit T. This is a type mismatch between the supplied value and the atom's declared type.

Source

Thrown at tsunami/engine/atomimpl.go:67

		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) {
	a.lock.Lock()
	defer a.lock.Unlock()
	if used {
		a.usedBy[waveId] = true
	} else {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Pass a value of the atom's exact type T so the round-trip is trivially compatible
  2. Verify field names/tags and types of the value match T's JSON unmarshaling requirements
  3. Wrap with a type check or use the atom's dedicated typed setter if available
  4. Log the wrapped error's %w detail to see the exact offending field

Example fix

// before
portAtom.SetVal("8080") // atom is Atom[int]
// after
port, _ := strconv.Atoi("8080")
portAtom.SetVal(port)
Defensive patterns

Strategy: type-guard

Validate before calling

if _, ok := val.(T); !ok {
    b, _ := json.Marshal(val)
    var probe T
    if err := json.Unmarshal(b, &probe); err != nil {
        return fmt.Errorf("value incompatible with atom type %T: %w", probe, err)
    }
}
atom.SetVal(val)

Type guard

func fitsAtomType[T any](val any) bool {
    if _, ok := val.(T); ok { return true }
    b, err := json.Marshal(val)
    if err != nil { return false }
    var probe T
    return json.Unmarshal(b, &probe) == nil
}

Try / catch

if err := atom.SetVal(val); err != nil {
    var typeErr *json.UnmarshalTypeError
    if errors.As(err, &typeErr) {
        log.Printf("field %s type mismatch: %v", typeErr.Field, typeErr.Error())
    }
}

Prevention

When it happens

Trigger: SetVal on an atom of type T with a value whose JSON does not unmarshal into T — e.g. setting a string into an atom of int, wrong field types/missing required non-pointer fields, JSON number into a struct field of a different kind.

Common situations: Refactoring T's struct without updating callers; setting atoms from JSON-decoded generic maps; passing float/string where T expects int/bool.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/9aa81ef54f62b261. Report an issue: GitHub.