dapr/dapr · error

error parsing %s due time: %w

Error message

error parsing %s due time: %w

What it means

setReminderTimes parses dueTime with timeutils.ParseTime. That helper accepts either an RFC3339 timestamp ("2025-01-02T15:04:05Z") or a Go duration relative to now ("90s", "1h"). logMsg tells you whether the failing object is a reminder or a timer. An invalid dueTime string produces this error.

Source

Thrown at pkg/actors/api/reminders.go:188

	if len(m.Data) > 0 {
		req.Data, err = anypb.New(wrapperspb.Bytes(m.Data))
		if err != nil {
			return fmt.Errorf("failed to unmarshal data: %w", err)
		}
	}

	return nil
}

func setReminderTimes(reminder *Reminder, dueTime string, period string, ttl string, now time.Time, logMsg string) (err error) {
	// Due time and registered time
	reminder.RegisteredTime = now
	reminder.DueTime = dueTime
	if dueTime != "" {
		reminder.RegisteredTime, err = parseReminderTime(dueTime, &now)
		if err != nil {
			return fmt.Errorf("error parsing %s due time: %w", logMsg, err)
		}
	}

	// Parse period and check correctness
	reminder.Period, err = NewReminderPeriod(period)
	if err != nil {
		return fmt.Errorf("invalid %s period: %w", logMsg, err)
	}

	// Set expiration time if configured
	if ttl != "" {
		reminder.ExpirationTime, err = parseReminderTime(ttl, &reminder.RegisteredTime)
		if err != nil {
			return fmt.Errorf("error parsing %s TTL: %w", logMsg, err)
		}
		// check if already expired
		if now.After(reminder.ExpirationTime) || reminder.RegisteredTime.After(reminder.ExpirationTime) {
			return fmt.Errorf("%s %s has already expired: dueTime: %s TTL: %s",

View on GitHub (pinned to 74ad417027)

Solutions

  1. Use an RFC3339 string with timezone ("2025-01-02T15:04:05Z") or a Go duration ("5m").
  2. Omit dueTime (empty string) to fire at registration time.
  3. Validate on the client with the same rule: try time.Parse(time.RFC3339, v), then time.ParseDuration(v).

Example fix

// before
{"dueTime":"2025-01-02 15:04:05","period":"30s"}

// after
{"dueTime":"2025-01-02T15:04:05Z","period":"30s"}
Defensive patterns

Strategy: validation

Validate before calling

import timeutils "github.com/dapr/kit/time"

func validReminderTime(v string) bool {
	if v == "" {
		return true
	}
	now := time.Now()
	_, err := timeutils.ParseTime(v, &now)
	return err == nil
}

Try / catch

if err := client.RegisterReminder(ctx, req); err != nil {
	if strings.Contains(err.Error(), "due time") {
		// bad dueTime string: fix input; parse failures are permanent
	}
}

Prevention

When it happens

Trigger: CreateReminder/CreateTimer, or the HTTP APIs POST /actors/{type}/{id}/reminders and /timers, sends dueTime that is neither RFC3339 nor a Go duration: "tomorrow", "1hour", "2025-01-02 15:04:05" (missing T and zone), or an empty placeholder like "null".

Common situations: A date is formatted without the T separator or timezone offset. A relative value spells out a unit Go rejects. A UI passes free-text input straight into the request.

Related errors


AI-assisted analysis of dapr/dapr@74ad417027 (2026-08-16). Data as JSON: /api/errors/cbdf4a2b43821fef. Report an issue: GitHub.