{"record":{"id":"9aa81ef54f62b261","repo":"wavetermdev/waveterm","slug":"failed-to-adapt-type-from-t-t-w","errorCode":null,"errorMessage":"failed to adapt type from %T => %T: %w","messagePattern":"failed to adapt type from %T => %T: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tsunami/engine/atomimpl.go","lineNumber":67,"sourceCode":"\t\treturn nil\n\t}\n\n\t// Try direct assignment if it's already type T\n\tif typed, ok := val.(T); ok {\n\t\ta.val = typed\n\t\treturn nil\n\t}\n\n\t// Try JSON marshaling/unmarshaling\n\tjsonBytes, err := json.Marshal(val)\n\tif err != nil {\n\t\tvar result T\n\t\treturn fmt.Errorf(\"failed to adapt type from %T => %T, input type failed to marshal: %w\", val, result, err)\n\t}\n\n\tvar result T\n\tif err := json.Unmarshal(jsonBytes, &result); err != nil {\n\t\treturn fmt.Errorf(\"failed to adapt type from %T => %T: %w\", val, result, err)\n\t}\n\n\ta.val = result\n\treturn nil\n}\n\nfunc (a *AtomImpl[T]) SetVal(val any) error {\n\ta.lock.Lock()\n\tdefer a.lock.Unlock()\n\treturn a.setVal_nolock(val)\n}\n\nfunc (a *AtomImpl[T]) SetUsedBy(waveId string, used bool) {\n\ta.lock.Lock()\n\tdefer a.lock.Unlock()\n\tif used {\n\t\ta.usedBy[waveId] = true\n\t} else {","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/tsunami/engine/atomimpl.go#L49-L85","documentation":"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.","triggerScenarios":"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.","commonSituations":"Refactoring T's struct without updating callers; setting atoms from JSON-decoded generic maps; passing float/string where T expects int/bool.","solutions":["Pass a value of the atom's exact type T so the round-trip is trivially compatible","Verify field names/tags and types of the value match T's JSON unmarshaling requirements","Wrap with a type check or use the atom's dedicated typed setter if available","Log the wrapped error's %w detail to see the exact offending field"],"exampleFix":"// before\nportAtom.SetVal(\"8080\") // atom is Atom[int]\n// after\nport, _ := strconv.Atoi(\"8080\")\nportAtom.SetVal(port)","handlingStrategy":"type-guard","validationCode":"if _, ok := val.(T); !ok {\n    b, _ := json.Marshal(val)\n    var probe T\n    if err := json.Unmarshal(b, &probe); err != nil {\n        return fmt.Errorf(\"value incompatible with atom type %T: %w\", probe, err)\n    }\n}\natom.SetVal(val)","typeGuard":"func fitsAtomType[T any](val any) bool {\n    if _, ok := val.(T); ok { return true }\n    b, err := json.Marshal(val)\n    if err != nil { return false }\n    var probe T\n    return json.Unmarshal(b, &probe) == nil\n}","tryCatchPattern":"if err := atom.SetVal(val); err != nil {\n    var typeErr *json.UnmarshalTypeError\n    if errors.As(err, &typeErr) {\n        log.Printf(\"field %s type mismatch: %v\", typeErr.Field, typeErr.Error())\n    }\n}","preventionTips":["Match the value's type to the atom's generic parameter","Update all SetVal call sites when changing T's struct shape","Test round-trip compatibility in unit tests"],"tags":["go","json","type-mismatch","generics"],"backgroundTag":"type-adaptation-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}