mikefarah/yq · error

%v (%v) cannot be subtracted from %v

Error message

%v (%v) cannot be subtracted from %v

What it means

When the lhs of a comparison is a scalar but the RIGHT-hand side is not a scalar (e.g. it is a map or array), yq cannot compare them and reports this error. The message text says 'cannot be subtracted' for historical reasons (the format string is shared), but the operator is actually the comparison (<, <=, >, >=). The error includes the rhs tag and path plus the lhs tag to pinpoint the mismatch.

Source

Thrown at pkg/yqlib/operator_compare.go:41

		if lhs == nil && rhs == nil {
			owner := &CandidateNode{}
			return createBooleanCandidate(owner, prefs.OrEqual), nil
		} else if lhs == nil {
			log.Debugf("lhs nil, but rhs is not")
			return createBooleanCandidate(rhs, false), nil
		} else if rhs == nil {
			log.Debugf("rhs nil, but rhs is not")
			return createBooleanCandidate(lhs, false), nil
		}

		switch lhs.Kind {
		case MappingNode:
			return nil, fmt.Errorf("maps not yet supported for comparison")
		case SequenceNode:
			return nil, fmt.Errorf("arrays not yet supported for comparison")
		default:
			if rhs.Kind != ScalarNode {
				return nil, fmt.Errorf("%v (%v) cannot be subtracted from %v", rhs.Tag, rhs.GetNicePath(), lhs.Tag)
			}
			target := lhs.CopyWithoutContent()
			boolV, err := compareScalars(context, prefs, lhs, rhs)

			return createBooleanCandidate(target, boolV), err
		}
	}
}

func compareDateTime(layout string, prefs compareTypePref, lhs *CandidateNode, rhs *CandidateNode) (bool, error) {
	lhsTime, err := parseDateTime(layout, lhs.Value)
	if err != nil {
		return false, err
	}

	rhsTime, err := parseDateTime(layout, rhs.Value)
	if err != nil {
		return false, err

View on GitHub (pinned to 8b5af0694b)

Solutions

  1. Check what the rhs resolves to: `yq '.items | type' file.yaml`, and navigate to the intended scalar field.
  2. If testing membership, use `contains` or `any`: `(.items | any(. == 3))` instead of `3 < .items`.
  3. If comparing counts, wrap arrays in `length`: `.count >= (.items | length)`.
  4. Verify input schema (e.g. with `yq 'paths(type == "!!seq")'`) if the field was expected to be a scalar.

Example fix

# before
yq '.replicas > .servers' file.yaml   # .servers is an array
# after
yq '.replicas > (.servers | length)' file.yaml
Defensive patterns

Strategy: validation

Validate before calling

# confirm both comparison operands are scalars before running:
yq -e '((.replicas | type) == "!!scalar" and (.servers | type) == "!!scalar")' file.yaml || echo "non-scalar operand"

Type guard

// yq expression guard: assert rhs is scalar
// yq '. as $l | .servers as $r | select(($r | tag) == "!!int" or ($r | tag) == "!!float" or ($r | tag) == "!!str") | $l.replicas > $r' file.yaml

Prevention

When it happens

Trigger: Expressions like `yq '3 < .tags' file.yaml` or `yq '.count >= .items'` where the rhs path resolves to a non-scalar (MappingNode or SequenceNode) while the lhs is a scalar. Also triggered via crossFunction when one side of a cross-product comparison resolves to a map/array.

Common situations: Typos in the rhs path that land on a parent object instead of a leaf field; schema changes where a field became an array; comparing a scalar against a whole list intending 'is it in the list' (should be `any`/`contains`); accidentally comparing across document types.

Related errors


AI-assisted analysis of mikefarah/yq@8b5af0694b (2026-09-05). Data as JSON: /api/errors/2e939dea79556991. Report an issue: GitHub.