{"record":{"id":"c3e3f1e5457f48a6","repo":"wavetermdev/waveterm","slug":"failed-to-adapt-type-from-t-t-input-type-fai","errorCode":null,"errorMessage":"failed to adapt type from %T => %T, input type failed to marshal: %w","messagePattern":"failed to adapt type from %T => %T, input type failed to marshal: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tsunami/engine/atomimpl.go","lineNumber":62,"sourceCode":"\nfunc (a *AtomImpl[T]) setVal_nolock(val any) error {\n\tif val == nil {\n\t\tvar zero T\n\t\ta.val = zero\n\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) {","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/tsunami/engine/atomimpl.go#L44-L80","documentation":"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.","triggerScenarios":"Calling atom.SetVal with a value containing a func, channel, complex number, or a struct with cyclic references — json.Marshal fails immediately.","commonSituations":"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.","solutions":["Pass only JSON-serializable values to SetVal (strip funcs/channels into separate fields)","Add `json:\"-\"` tags to non-serializable struct fields","Pre-serialize to a plain type (map[string]any or a DTO struct) before SetVal","Consider matching the atom's type T exactly so no adaptation is needed"],"exampleFix":"// before\natom.SetVal(Config{Callback: myFunc}) // funcs not marshalable\n// after\natom.SetVal(Config{Name: cfg.Name}) // JSON-safe fields only","handlingStrategy":"validation","validationCode":"func jsonSafe(v any) error {\n    _, err := json.Marshal(v)\n    return err\n}\nif err := jsonSafe(val); err != nil {\n    return fmt.Errorf(\"value not settable on atom: %w\", err)\n}\natom.SetVal(val)","typeGuard":"func isJSONSerializable(v any) bool {\n    switch v.(type) {\n    case chan struct{}, func(), complex128:\n        return false\n    }\n    return jsonSafe(v) == nil\n}","tryCatchPattern":"if err := atom.SetVal(val); err != nil {\n    if strings.Contains(err.Error(), \"failed to marshal\") {\n        log.Printf(\"value %T is not JSON-serializable\", val)\n    }\n}","preventionTips":["Never store funcs/channels/cyclic data in atoms","Tag non-serializable struct fields with `json:\"-\"`","Prefer setting values of the atom's exact type T"],"tags":["go","json","serialization","generics"],"backgroundTag":"json-marshal-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}