{"record":{"id":"da10d8d540da97dc","repo":"mikefarah/yq","slug":"strings-cannot-be-subtracted","errorCode":null,"errorMessage":"strings cannot be subtracted","messagePattern":"strings cannot be subtracted","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/yqlib/operator_subtract.go","lineNumber":97,"sourceCode":"\t\tlhsIsCustom = true\n\t}\n\n\tif !strings.HasPrefix(rhsTag, \"!!\") {\n\t\t// custom tag - we have to have a guess\n\t\trhsTag = rhs.guessTagFromCustomType()\n\t}\n\n\tisDateTime := lhsTag == \"!!timestamp\"\n\t// if the lhs is a string, it might be a timestamp in a custom format.\n\tif lhsTag == \"!!str\" && context.GetDateTimeLayout() != time.RFC3339 {\n\t\t_, err := parseDateTime(context.GetDateTimeLayout(), lhs.Value)\n\t\tisDateTime = err == nil\n\t}\n\n\tif isDateTime {\n\t\treturn subtractDateTime(context.GetDateTimeLayout(), target, lhs, rhs)\n\t} else if lhsTag == \"!!str\" {\n\t\treturn fmt.Errorf(\"strings cannot be subtracted\")\n\t} else if lhsTag == \"!!int\" && rhsTag == \"!!int\" {\n\t\tformat, lhsNum, err := parseInt64(lhs.Value)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, rhsNum, err := parseInt64(rhs.Value)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresult := lhsNum - rhsNum\n\t\ttarget.Tag = lhs.Tag\n\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)","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/mikefarah/yq/blob/8b5af0694bb82b41d4ae180fac9972029066f90a/pkg/yqlib/operator_subtract.go#L79-L115","documentation":"yq refuses to subtract strings: unlike jq (which errors differently) or some shells, `-` on `!!str` operands has no meaning in yq, so subtractScalars returns this error whenever the resolved lhs tag is `!!str` and the value is not being treated as a datetime. Note the asymmetry in the message: it is raised from the subtract path even though the text says 'subtracted'.","triggerScenarios":"`yq '.name - .other'` where both fields are strings; `.a - .b` where .a is a quoted number like `\"10\"` (tag !!str) and no custom datetime layout applies; custom-typed nodes guessed as strings.","commonSituations":"YAML files with quoted numbers (e.g. version: \"1.2\") that the user expects to be numeric; attempting string 'removal' like removing a prefix (substrings are not supported by `-`); fields tagged !!str due to explicit quoting in the source document.","solutions":["Convert strings to numbers first with `to_number` (or `| tonumber`-style): `(.a | to_number) - (.b | to_number)`","Unquote the values in the source YAML so they parse as !!int/!!float","For string manipulation use the appropriate string operators/functions (sub/split, capture, etc.), not `-`","If the string is actually a datetime, set the right layout with the datetime layout option (via `env`/timestamp functions) so it takes the datetime path"],"exampleFix":"// before: yq '\"10\" - \"4\"'  -> strings cannot be subtracted\n// after\nyq '(\"10\" | to_number) - (\"4\" | to_number)'","handlingStrategy":"type-guard","validationCode":"tag_lhs=$(yq '.a | tag' file.yml)\n[ \"$tag_lhs\" = \"!!str\" ] && echo \"strings cannot be subtracted; convert with to_number\" && exit 1","typeGuard":"// Go-side guard before building the expression\nfunc isNumericScalar(s string) bool {\n    _, errInt := strconv.ParseInt(s, 10, 64)\n    _, errFloat := strconv.ParseFloat(s, 64)\n    return errInt == nil || errFloat == nil\n}","tryCatchPattern":"null","preventionTips":["Avoid quoting numbers in YAML unless intentional","Use `to_number` on string-typed numerics before arithmetic","Use string functions (sub/capture) instead of `-` for string manipulation"],"tags":["yq","strings","subtract-operator","type-mismatch"],"backgroundTag":"unsupported-string-arithmetic","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"}