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

estimated values must start with the exact prefix #{ESTIMATE

Error message

estimated values must start with the exact prefix #{ESTIMATED_PREFIX.inspect}

What it means

Provenance::Citation.parse! raises InvalidError when the string matches the ESTIMATED_MARKER (starts with 'estimated' followed by optional whitespace and a colon, case-insensitive) but does not start with the exact prefix 'estimated: ' (lowercase, single space). This stops variants like 'Estimated:', 'estimated :', or 'ESTIMATED:' so an estimate can never lose its canonical marker on the way in.

Source

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

    # citation text and passing as an ungraded — but plausible-looking — source.
    GRADE_SUFFIX = /\(grade:\s*(?<grade>[^)]*)\)\s*\z/
    ESTIMATED_MARKER = /\Aestimated\s*:/i

    class InvalidError < StandardError; end

    attr_reader :raw, :text, :grade

    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?

View on GitHub (pinned to e69894adb9)

Solutions

  1. Use the exact prefix: start the citation with 'estimated: ' (lowercase, one space) — e.g. 'estimated: linear interpolation over anchors (grade: C)'.
  2. Normalize before parsing: strip, downcase just the marker, or value.sub(/\Aestimated\s*:/i, 'estimated: ').
  3. Include the grade suffix, since estimates also require a grade.
  4. Test with Provenance::Citation.valid? first and show the grammar in the error UI.

Example fix

# before
Provenance::Citation.parse!("Estimated: interpolated from 2024 anchors (grade: C)")

# after
normalized = raw.to_s.strip.sub(/\Aestimated\s*:/i, "estimated: ")
Provenance::Citation.parse!(normalized)
Defensive patterns

Strategy: validation

Validate before calling

v = value.to_s
!v.match?(/\Aestimated\s*:/i) || v.start_with?("estimated: ")

Type guard

def estimated_prefix_ok?(v)
  v = v.to_s.strip
  !v.match?(Provenance::Citation::ESTIMATED_MARKER) || v.start_with?(Provenance::Citation::ESTIMATED_PREFIX)
end

Try / catch

begin
  Provenance::Citation.parse!(source)
rescue Provenance::Citation::InvalidError => e
  source = source.sub(/\Aestimated\s*:/i, "estimated: ") # normalize and retry once
  retry
end

Prevention

When it happens

Trigger: Citation beginning 'Estimated: ...', 'estimated : ...', 'ESTIMATED: ...', or 'estimated:\t...'; agent writing the marker with different casing or spacing.

Common situations: LLM capitalizing the prefix; human typing 'Estimated:'; a template that trims or reformats the leading prefix.

Related errors


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