we-promise/sure · error · Provenance::Citation::InvalidError

source citation does not match #{grammar}

Error message

source citation does not match #{grammar}

What it means

Provenance::Citation.parse! raises InvalidError 'source citation does not match ...grammar' when the whole string fails the FORMAT regex: optional 'estimated: ' prefix, then citation text, then optional ' (grade: A|B|C)' suffix. Because earlier checks already handled blank, length, marker, and grade issues, hitting this usually means structural problems such as unbalanced parentheses that confuse the suffix match.

Source

Thrown at app/models/provenance/citation.rb:55

    class << self
      # Returns a Citation, or raises InvalidError with a message written for the
      # caller that has to fix it (an agent, usually).
      def parse!(raw)
        value = raw.to_s.strip

        raise InvalidError, "source citation is required" if value.blank?
        raise InvalidError, "source citation must be #{MAX_LENGTH} characters or fewer" if value.length > MAX_LENGTH

        if value.match?(ESTIMATED_MARKER) && !value.start_with?(ESTIMATED_PREFIX)
          raise InvalidError, "estimated values must start with the exact prefix #{ESTIMATED_PREFIX.inspect}"
        end

        if (suffix = value.match(GRADE_SUFFIX)) && !GRADES.include?(suffix[:grade])
          raise InvalidError, "reliability grade must be one of #{GRADES.join(", ")} (got #{suffix[:grade].inspect})"
        end

        match = value.match(FORMAT)
        raise InvalidError, "source citation does not match #{grammar}" unless match

        text = match[:text].to_s.strip
        if text.length < MIN_TEXT_LENGTH
          raise InvalidError, "source citation must name the document it came from"
        end

        estimated = match[:estimated].present?
        grade = match[:grade]

        if estimated && grade.blank?
          raise InvalidError, "estimated values must carry a reliability grade, e.g. \"#{ESTIMATED_PREFIX}... (grade: C)\""
        end

        new(raw: value, text: text, estimated: estimated, grade: grade)
      end

      def valid?(raw)
        parse!(raw)

View on GitHub (pinned to e69894adb9)

Solutions

  1. Simplify to: document name, optionally ' (grade: X)' at the very end, e.g. 'Audited report 2025, p.12 (grade: B)'.
  2. Remove or balance parentheses that are not the grade suffix.
  3. Print Provenance::Citation.grammar in error output so the caller can self-correct: ["estimated: "] citation [" (grade: A|B|C)"].
  4. Round-trip check: parse, then to_s, then parse again.

Example fix

# before
Provenance::Citation.parse!("Payroll summary (Q1 (grade: B)")

# after
Provenance::Citation.parse!("Payroll summary Q1 (grade: B)")
Defensive patterns

Strategy: try-catch

Validate before calling

Provenance::Citation.valid?(value)

Try / catch

begin
  Provenance::Citation.parse!(source)
rescue Provenance::Citation::InvalidError => e
  # e.message embeds Provenance::Citation.grammar; surface it for self-correction
  correct_and_retry(source, e.message)
end

Prevention

When it happens

Trigger: Citation like 'Statement (unaudited' (unbalanced paren), 'Doc ((grade: A))', trailing ')', or a string whose only content is consumed leaving no text; also text consisting solely of a grade suffix.

Common situations: Parentheses in the document name interacting with the grade suffix; agent output with stray parens; markdown emphasis leaking in.

Related errors


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