{"record":{"id":"d29d6764215333dc","repo":"wavetermdev/waveterm","slug":"failed-to-marshal-input-w-d29d67","errorCode":null,"errorMessage":"failed to marshal input: %w","messagePattern":"failed to marshal input: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/tools_tsunami.go","lineNumber":98,"sourceCode":"}\n\nfunc makeTsunamiPostCallback(status *blockcontroller.BlockControllerRuntimeStatus, apiPath string) func(any, *uctypes.UIMessageDataToolUse) (any, error) {\n\treturn func(input any, toolUseData *uctypes.UIMessageDataToolUse) (any, error) {\n\t\tif status.TsunamiPort == 0 {\n\t\t\treturn nil, fmt.Errorf(\"tsunami port not available\")\n\t\t}\n\n\t\turl := fmt.Sprintf(\"http://localhost:%d%s\", status.TsunamiPort, apiPath)\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\tdefer cancel()\n\n\t\tvar reqBody []byte\n\t\tvar err error\n\t\tif input != nil {\n\t\t\treqBody, err = json.Marshal(input)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to marshal input: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\treq, err := http.NewRequestWithContext(ctx, \"POST\", url, strings.NewReader(string(reqBody)))\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to create request: %w\", err)\n\t\t}\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\tresp, err := http.DefaultClient.Do(req)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to make request to tsunami: %w\", err)\n\t\t}\n\t\tdefer resp.Body.Close()\n\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\treturn nil, fmt.Errorf(\"tsunami returned status %d\", resp.StatusCode)\n\t\t}","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/tools_tsunami.go#L80-L116","documentation":"This error wraps json.Marshal failure when serializing the tool's input argument before POSTing it to the Tsunami widget's /api/config endpoint. json.Marshal fails only for values JSON cannot represent (channels, funcs, cycles, or unsupported types). The underlying marshal error is preserved via %w.","triggerScenarios":"Calling tsunami_setconfig with an input value containing non-JSON-serializable data (e.g. a map holding a func or channel, cyclic structures) - though inputs normally arrive as decoded JSON from the model, hand-crafted or programmatic callers can pass arbitrary any values.","commonSituations":"Programmatic/tool-harness callers passing Go-native values with unsupported types instead of plain JSON-compatible maps; custom tool runners injecting config objects containing cyclic references or unserializable field types.","solutions":["Read the wrapped error to identify the offending type, then ensure the input passed to the tool callback is JSON-compatible (plain maps, slices, strings, numbers, bools).","Marshal the input yourself with json.Marshal (or utilfn.ReUnmarshal) before invoking the callback to catch the problem early with a clearer stack.","If input comes from a non-JSON source, convert it to a JSON-safe representation first.","For complex config values, register MarshalJSON on the custom type so it serializes correctly."],"exampleFix":"// before: callback(map[string]any{\"cb\": func() {}}) fails with 'json: unsupported type: func()'; // after: callback(map[string]any{\"refreshInterval\": 30}) with JSON-safe input only","handlingStrategy":"validation","validationCode":"if _, err := json.Marshal(input); err != nil { return fmt.Errorf(\"tool input is not JSON-serializable: %w\", err) }","typeGuard":"func isJSONSafe(v any) bool { _, err := json.Marshal(v); return err == nil }","tryCatchPattern":"out, err := callback(input, toolUseData); if err != nil && strings.Contains(err.Error(), \"failed to marshal input\") { /* sanitize input to a JSON-safe map and retry once */ }","preventionTips":["Pass only JSON-decoded values (maps, slices, strings, numbers, bools) into tool callbacks.","Pre-marshal inputs in tests to catch unsupported types early.","Avoid funcs/channels/cyclic references in config objects.","Use utilfn.ReUnmarshal to normalize inputs before invoking callbacks."],"tags":["json","serialization","tsunami"],"backgroundTag":"json-marshal-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}