we-promise/sure · warning · Assistant::Error

Invalid month: #{raw}. Use YYYY-MM or MMM-YYYY.

Error message

Invalid month: #{raw}. Use YYYY-MM or MMM-YYYY.

What it means

Raised by Assistant::Function::MonthResolvable#parse_month when the month argument does not strictly match \A\d{4}-\d{2}\z (YYYY-MM) or \A[A-Za-z]{3}-\d{4}\z (MMM-YYYY). The anchor check exists because Date.strptime ignores trailing characters ("2026-08-15" would silently parse with %Y-%m), so any other shape is rejected up front. UpdateBudget rescues Assistant::Error and returns { success: false, error: "invalid_params", message: ... }.

Source

Thrown at app/models/assistant/function/month_resolvable.rb:23

  private
    def resolve_month_start(raw)
      base = parse_month(raw)
      return (base || Date.current).beginning_of_month unless family.uses_custom_month_start?

      # Match Budget.param_to_date for explicit slugs so the input round-trips with the response.
      base ? Date.new(base.year, base.month, family.month_start_day) : family.custom_month_start_for(Date.current)
    end

    def parse_month(raw)
      return nil if raw.blank?

      # Date.strptime ignores trailing characters, so guard with strict anchors first.
      fmt = case raw
      when /\A\d{4}-\d{2}\z/         then "%Y-%m"
      when /\A[A-Za-z]{3}-\d{4}\z/   then "%b-%Y"
      end

      raise Assistant::Error, "Invalid month: #{raw}. Use YYYY-MM or MMM-YYYY." if fmt.nil?

      Date.strptime(raw, fmt)
    rescue ArgumentError
      raise Assistant::Error, "Invalid month: #{raw}. Use YYYY-MM or MMM-YYYY."
    end
end

View on GitHub (pinned to e69894adb9)

Solutions

  1. Pass the month in one of the two accepted shapes: "2026-08" or "Aug-2026" (three-letter abbreviation, hyphen, four-digit year).
  2. If the value comes from a Date object, normalize it first with date.strftime("%Y-%m") before calling the function.
  3. If you control the caller, pre-validate with the same anchored regexes and reformat instead of retrying blindly.

Example fix

# before
update_budget.call({ "month" => "August 2026", "budgeted_spending" => 6500 })

# after — normalize to the accepted slug first
month = Date.parse("August 2026").strftime("%Y-%m")
update_budget.call({ "month" => month, "budgeted_spending" => 6500 })
Defensive patterns

Strategy: validation

Validate before calling

MONTH_SLUG = /\A(?:\d{4}-\d{2}|[A-Za-z]{3}-\d{4})\z/
raise ArgumentError, "month must be YYYY-MM or MMM-YYYY" unless month.to_s.match?(MONTH_SLUG)

Type guard

# Ruby
def valid_month_slug?(raw)
  raw.is_a?(String) && raw.match?("\\A(?:\\d{4}-\\d{2}|[A-Za-z]{3}-\\d{4})\\z")
end

Try / catch

begin
  update_budget.call(params)
rescue Assistant::Error => e
  # call() already converts this to { success: false, error: "invalid_params" } — check the result instead
end

Prevention

When it happens

Trigger: Calling update_budget with month: "2026/08" (slash), "Aug 2026" (space instead of hyphen), "2026-8" (single-digit month), "202608", "last month", or "August 2026" — none match either anchored regex, so fmt is nil and the raise at app/models/assistant/function/month_resolvable.rb:23 fires.

Common situations: LLM free-forming a natural-language month instead of the documented format; user-typed values passed through unnormalized; months imported from other tools that use MM/DD or 'August 2026' styles.

Related errors


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/9634a46ea8fb5fc7. Report an issue: GitHub.