{"record":{"id":"d0853c7304dd273a","repo":"mikefarah/yq","slug":"v-cannot-be-added-to-v-d0853c","errorCode":null,"errorMessage":"%v cannot be added to %v","messagePattern":"(.+?) cannot be added to (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/yqlib/operator_subtract.go","lineNumber":127,"sourceCode":"\t\ttarget.Value = fmt.Sprintf(format, result)\n\t} else if (lhsTag == \"!!int\" || lhsTag == \"!!float\") && (rhsTag == \"!!int\" || rhsTag == \"!!float\") {\n\t\tlhsNum, err := strconv.ParseFloat(lhs.Value, 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\trhsNum, err := strconv.ParseFloat(rhs.Value, 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresult := lhsNum - rhsNum\n\t\tif lhsIsCustom {\n\t\t\ttarget.Tag = lhs.Tag\n\t\t} else {\n\t\t\ttarget.Tag = \"!!float\"\n\t\t}\n\t\ttarget.Value = fmt.Sprintf(\"%v\", result)\n\t} else {\n\t\treturn fmt.Errorf(\"%v cannot be added to %v\", lhs.Tag, rhs.Tag)\n\t}\n\n\treturn nil\n}\n\nfunc subtractDateTime(layout string, target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error {\n\tvar durationStr string\n\tif strings.HasPrefix(rhs.Value, \"-\") {\n\t\tdurationStr = rhs.Value[1:]\n\t} else {\n\t\tdurationStr = \"-\" + rhs.Value\n\t}\n\tduration, err := time.ParseDuration(durationStr)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to parse duration [%v]: %w\", rhs.Value, err)\n\t}\n","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/mikefarah/yq/blob/8b5af0694bb82b41d4ae180fac9972029066f90a/pkg/yqlib/operator_subtract.go#L109-L145","documentation":"Fallback error in subtractScalars for any tag combination that is not int-int, int-float, float-int, float-float, or datetime: yq cannot subtract these operands. Notably the message wording says 'cannot be added' — a known copy-paste artifact from the add operator — but it is raised by the subtract operator.","triggerScenarios":"Subtracting booleans (`true - false`), null rhs (`5 - null` after a missing key resolves oddly), maps/other custom tags that don't guess to numeric types, e.g. `yq '.enabled - .other'` on boolean fields, or `.a - .b` where .b is !!null and lhs isn't handled by the null shortcut.","commonSituations":"Boolean flags mistakenly used in arithmetic; missing keys producing null operands in expressions like `.count - .missing`; datetime handling when lhs tag isn't !!timestamp and no custom layout is configured; custom tags that guess to unexpected types.","solutions":["Check both operand tags with `yq '.a | tag'` and `.b | tag'`, and fix the mismatched one","Provide a numeric default for missing/null values: `.count - (.missing // 0)`","Convert with `to_number` where possible; booleans need explicit conversion logic before arithmetic","If the value is a datetime string, configure the correct datetime layout so it takes the subtractDateTime path"],"exampleFix":"// before: yq '.count - .missing' file.yml  -> error (.missing is null)\n// after\nyq '.count - (.missing // 0)' file.yml","handlingStrategy":"validation","validationCode":"// validate operand tags are numeric before subtracting\nfor f in .count .other; do\n  t=$(yq \"$f | tag\" file.yml)\n  case \"$t\" in '!!int'|'!!float') ;; *) echo \"$f is $t, not numeric\"; exit 1;; esac\ndone","typeGuard":"func isNumericTag(tag string) bool {\n    return tag == \"!!int\" || tag == \"!!float\"\n}","tryCatchPattern":"null","preventionTips":["Default null/missing operands: `.count - (.other // 0)`","Never use boolean fields in arithmetic expressions","Check `tag` output for custom-typed nodes to confirm they guess as numeric"],"tags":["yq","type-mismatch","subtract-operator","null"],"backgroundTag":"operand-type-mismatch","analyzedSha":"8b5af0694bb82b41d4ae180fac9972029066f90a","analyzedAt":"2026-09-05T10:57:22.766Z","contentChangedAt":"2026-09-05T10:57:22.766Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}