BoundaryML/baml · error · minijinja::Error

Cannot parse end date

Error message

Cannot parse end date

What it means

The date_between Jinja filter parses the row `date`, then the `begin` bound, then the `end` bound. If `end` cannot be parsed into a date by parse_date, the filter raises a minijinja SyntaxError 'Cannot parse end date'. Like the begin bound, an unparseable end is treated as a template error rather than silently false.

Solutions

  1. Correct the `end` argument to match the format expected by parse_date (e.g. YYYY-MM-DD)
  2. Pre-validate or normalize the end date in the template before calling date_between
  3. Check computed end expressions render to a parseable date string
  4. Ensure the end date is actually later-formatted correctly relative to begin (a swapped begin/end still needs valid formats)

Example fix

// before
{{ date_between(post.created_at, "2024-01-01", end_date_var) }}
// after (end_date_var = "31-12-2024" -> fix source)
{% set end_date_var = "2024-12-31" %}
{{ date_between(post.created_at, "2024-01-01", end_date_var) }}
Defensive patterns

Strategy: validation

Validate before calling

const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
if (!ISO_DATE.test(end)) throw new Error(`end must be YYYY-MM-DD, got: ${end}`);

Try / catch

{% if end is string and end|length == 10 %}
  {{ date_between(d, begin, end) }}
{% else %}
  {{ false }}
{% endif %}

Prevention

When it happens

Trigger: Calling date_between(date, begin, end) where `end` is not in the format parse_date accepts (wrong format, empty string, or a non-date expression).

Common situations: Templates with a mistyped or locale-formatted end date; computed end values (e.g. concatenated strings) that don't produce a valid date string.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/358f4350eddb537a. Report an issue: GitHub.

Appendix: source

Thrown at engine/boundary-udf/src/lib.rs:88

    }
}

// NOTE: (Jesus) Could use real date type
fn date_between(date: String, begin: String, end: String) -> Result<bool, minijinja::Error> {
    fn parse_date(date: &str) -> chrono::ParseResult<chrono::NaiveDate> {
        chrono::NaiveDate::parse_from_str(date, "%Y-%m-%d")
    }

    // NOTE: (Jesus) This will be parsing `begin` and `end` dates for all rows and `date` for all date
    // comparisons.
    let Ok(date) = parse_date(&date) else {
        return Ok(false);
    };
    let begin = parse_date(&begin).map_err(|_| {
        minijinja::Error::new(minijinja::ErrorKind::SyntaxError, "Cannot parse begin date")
    })?;
    let end = parse_date(&end).map_err(|_| {
        minijinja::Error::new(minijinja::ErrorKind::SyntaxError, "Cannot parse end date")
    })?;

    Ok(date >= begin && date <= end)
}

pub mod tests {
    //! Utilities for testing
    use crate::{config::UDFConfig, read_udf_config};

    pub fn load_sample_udf() -> UDFConfig {
        use anyhow::Context;
        read_udf_config("./sample-prices.yaml")
            .context("load sample UDF config")
            .unwrap()
    }

    // NOTE: sample data in here has been generated with the help of AI.
    pub mod data {

View on GitHub (pinned to bd85ce9dee)