mikefarah/yq · error

unable to parse duration [%v]: %w

Error message

unable to parse duration [%v]: %w

What it means

When subtracting from a datetime, the RIGHT operand must be a Go-parsable duration string (e.g. `1h30m`, `-300ms`). yq negates it and calls time.ParseDuration; if the string isn't a valid Go duration, this wrapped error is returned showing the offending value and the underlying parse error.

Source

Thrown at pkg/yqlib/operator_subtract.go:143

		target.Value = fmt.Sprintf("%v", result)
	} else {
		return fmt.Errorf("%v cannot be added to %v", lhs.Tag, rhs.Tag)
	}

	return nil
}

func subtractDateTime(layout string, target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error {
	var durationStr string
	if strings.HasPrefix(rhs.Value, "-") {
		durationStr = rhs.Value[1:]
	} else {
		durationStr = "-" + rhs.Value
	}
	duration, err := time.ParseDuration(durationStr)

	if err != nil {
		return fmt.Errorf("unable to parse duration [%v]: %w", rhs.Value, err)
	}

	currentTime, err := parseDateTime(layout, lhs.Value)
	if err != nil {
		return err
	}

	newTime := currentTime.Add(duration)
	target.Value = newTime.Format(layout)
	return nil
}

View on GitHub (pinned to 8b5af0694b)

Solutions

  1. Use Go duration syntax with units: `ns`, `us`, `ms`, `s`, `m`, `h` (compose, e.g. `24h` for a day)
  2. Replace `1day` with `24h`, `30 days` with `720h`
  3. To get the difference between two timestamps, subtract to get a duration contextually or compute via unix timestamps instead of passing a timestamp as the rhs duration
  4. Validate the duration string format before running the expression

Example fix

// before: yq 'now - 1day'  -> unable to parse duration [1day]
// after
yq 'now - 24h'
Defensive patterns

Strategy: validation

Validate before calling

// validate a Go-parseable duration before passing it to yq
import (
  "fmt"
  "time"
  "regexp"
)
var goDuration = regexp.MustCompile(`^-?[0-9]+(\.[0-9]+)?(ns|us|ms|s|m|h)$`)
func validateDuration(s string) error {
  if !goDuration.MatchString(s) {
    return fmt.Errorf("not a Go duration: %s (use ns/us/ms/s/m/h units)", s)
  }
  _, err := time.ParseDuration(s)
  return err
}

Type guard

func isGoDuration(s string) bool {
    _, err := time.ParseDuration(s)
    return err == nil
}

Try / catch

// Go caller wrapping yq run
if err := runYq(expr); err != nil {
    var durErr *fmt.WrapError
    if strings.Contains(err.Error(), "unable to parse duration") {
        return fmt.Errorf("fix duration to Go syntax (h/m/s), e.g. 24h: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: `yq 'now - 1day'` (Go durations don't support 'day'), `.createdAt - "2 weeks"`, or subtracting a non-duration value like a number or timestamp from a datetime field: `.start - .end` where both are timestamps.

Common situations: Using human-friendly durations (`1d`, `2w`, `30 days`) that Go's ParseDuration rejects; forgetting units (`1h` works, `1` doesn't); accidentally subtracting a second timestamp instead of a duration when computing a difference of dates.

Understand the failure class

Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.

Related errors


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