{"record":{"id":"925583224136490f","repo":"wavetermdev/waveterm","slug":"atom-s-in-s-s","errorCode":null,"errorMessage":"atom %s: in %s: %s","messagePattern":"atom (.+?): in (.+?): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tsunami/util/util.go","lineNumber":148,"sourceCode":"func implementsJSON(t reflect.Type) bool {\n\tif t.Implements(jsonMarshalerT) || t.Implements(textMarshalerT) {\n\t\treturn true\n\t}\n\tif t.Kind() != reflect.Pointer {\n\t\tpt := reflect.PointerTo(t)\n\t\treturn pt.Implements(jsonMarshalerT) || pt.Implements(textMarshalerT)\n\t}\n\treturn false\n}\n\nfunc ValidateAtomType(t reflect.Type, atomName string) error {\n\tseen := make(map[reflect.Type]bool)\n\treturn validateAtomTypeRecursive(t, seen, atomName, \"\")\n}\n\nfunc makeAtomError(atomName string, parentName string, message string) error {\n\tif parentName != \"\" {\n\t\treturn fmt.Errorf(\"atom %s: in %s: %s\", atomName, parentName, message)\n\t}\n\treturn fmt.Errorf(\"atom %s: %s\", atomName, message)\n}\n\nfunc validateAtomTypeRecursive(t reflect.Type, seen map[reflect.Type]bool, atomName string, parentName string) error {\n\tif t == nil {\n\t\treturn makeAtomError(atomName, parentName, \"nil type\")\n\t}\n\n\tif seen[t] {\n\t\treturn nil\n\t}\n\tseen[t] = true\n\n\t// Check if type implements json.Marshaler or encoding.TextMarshaler\n\tif implementsJSON(t) {\n\t\treturn nil\n\t}","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/tsunami/util/util.go#L130-L166","documentation":"makeAtomError builds a contextual error for ValidateAtomType failures. With a non-empty parentName it prefixes the message with both the atom name and the containing field path, produced while recursively validating that an atom's type is JSON-serializable (all fields marshalable, no invalid types like chan/func in the tree).","triggerScenarios":"Calling util.ValidateAtomType(reflect.TypeOf(myStruct), \"myatom\") where a nested field (named in parentName) has a type that fails validation — e.g. contains a chan, func, or unsafe.Pointer field without a MarshalJSON/TextMarshaler implementation.","commonSituations":"Adding a new field to an atom struct that holds a channel, function value, or other non-serializable type; registering a new atom type with an unsupported nested struct.","solutions":["Fix the type named in the 'in <parent>' part of the message: remove or replace the non-serializable field.","Implement MarshalJSON (or encoding.TextMarshaler) on the offending type so validation accepts it.","Wrap the field in a serializable type (e.g. store a channel identifier string instead of the channel itself)."],"exampleFix":"// before\ntype Atom struct { Events chan int `json:\"events\"` } // invalid\n// after\ntype Atom struct { Events []int `json:\"events\"` }\n// or: func (c ChanWrapper) MarshalJSON() ([]byte, error) { ... }","handlingStrategy":"validation","validationCode":"func checkSerializable(v any) error {\n    _, err := json.Marshal(v)\n    return err\n}","typeGuard":null,"tryCatchPattern":"if err := util.ValidateAtomType(reflect.TypeOf(atom), \"myatom\"); err != nil {\n    return fmt.Errorf(\"atom registration rejected: %w\", err)\n}","preventionTips":["Avoid chan/func/unsafe.Pointer fields in atom structs","Implement MarshalJSON on custom types used inside atoms","Run ValidateAtomType in unit tests at package init so new fields are caught immediately"],"tags":["validation","serialization","reflection","go"],"backgroundTag":"atom-type-validation-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}