{"record":{"id":"4635325415115545","repo":"wavetermdev/waveterm","slug":"setpath-remove-and-combinefn-are-mutually-exclusi","errorCode":null,"errorMessage":"SetPath: Remove and CombineFn are mutually exclusive","messagePattern":"SetPath: Remove and CombineFn are mutually exclusive","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/ijson/ijson.go","lineNumber":234,"sourceCode":"\ntype SetPathOpts struct {\n\tBudget    int // Budget 0 is unlimited (to set a 0 value, use -1)\n\tForce     bool\n\tRemove    bool\n\tCombineFn CombiningFunc\n}\n\nfunc SetPathNoErr(data any, path Path, value any, opts *SetPathOpts) any {\n\tret, _ := SetPath(data, path, value, opts)\n\treturn ret\n}\n\nfunc SetPath(data any, path Path, value any, opts *SetPathOpts) (any, error) {\n\tif opts == nil {\n\t\topts = &SetPathOpts{}\n\t}\n\tif opts.Remove && opts.CombineFn != nil {\n\t\treturn nil, fmt.Errorf(\"SetPath: Remove and CombineFn are mutually exclusive\")\n\t}\n\tif opts.Remove && value != nil {\n\t\treturn nil, fmt.Errorf(\"SetPath: Remove and value are mutually exclusive\")\n\t}\n\treturn setPathInternal(data, pathWithPos{Path: path, Index: 0}, value, *opts)\n}\n\nfunc checkAndModifyBudget(opts *SetPathOpts, pp pathWithPos, cost int) bool {\n\tif opts.Budget == 0 {\n\t\treturn true\n\t}\n\topts.Budget -= cost\n\tif opts.Budget < 0 {\n\t\treturn false\n\t}\n\tif opts.Budget == 0 {\n\t\t// 0 is weird since it means unlimited, so we set it to -1 to fail the next operation\n\t\topts.Budget = -1","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/ijson/ijson.go#L216-L252","documentation":"SetPath validates SetPathOpts before applying a path operation. Because Remove deletes the value at the path, it makes no sense to also supply CombineFn (which merges an existing value with a new one), so supplying both is rejected outright with this error. It is a pure API-misuse guard — the data is untouched.","triggerScenarios":"Calling SetPath with opts where opts.Remove == true AND opts.CombineFn != nil, e.g. ijson.SetPath(data, path, nil, &ijson.SetPathOpts{Remove: true, CombineFn: func(a, b any) any {...}}). Callers SetPathNoErr, ApplyCommand, and TestSetPath funnel through here so any of them can surface it.","commonSituations":"Options structs assembled dynamically (flags/config) where Remove and a combine callback are both set by accident; copy-pasted option literals extended with Remove without removing CombineFn.","solutions":["Remove CombineFn from the options when doing a delete, keeping only Remove: true.","Remove the Remove flag when you intend to merge with CombineFn.","If options come from user flags, validate exclusivity at the CLI/config layer before calling SetPath."],"exampleFix":"// before\nopts := &ijson.SetPathOpts{Remove: true, CombineFn: combineFn}\n_, err := ijson.SetPath(data, path, nil, opts) // mutually exclusive\n// after\nopts := &ijson.SetPathOpts{Remove: true} // delete: no CombineFn, no value\n_, err := ijson.SetPath(data, path, nil, opts)","handlingStrategy":"validation","validationCode":"func safeOpts(opts *ijson.SetPathOpts) error {\n    if opts != nil && opts.Remove && opts.CombineFn != nil {\n        return errors.New(\"Remove and CombineFn cannot be used together\")\n    }\n    return nil\n}\nif err := safeOpts(opts); err != nil { return err }","typeGuard":null,"tryCatchPattern":"result, err := ijson.SetPath(data, path, value, opts)\nif err != nil {\n    if strings.Contains(err.Error(), \"mutually exclusive\") {\n        opts.CombineFn = nil // fall back to plain removal\n        result, err = ijson.SetPath(data, path, nil, opts)\n    }\n    if err != nil { return err }\n}","preventionTips":["Treat Remove as a mode of its own: when set, force value=nil and CombineFn=nil.","Centralize option construction in one helper so exclusivity is enforced in one place.","Add a unit test for every combination of SetPathOpts fields you allow.","When options come from flags/config, validate exclusivity at parse time with a clear user-facing message."],"tags":["api-misuse","options","validation"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}