You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[BUG] SQL date_format function returns null for valid inputs on 2.19.0 #1
When running a SQL query that uses date_format on a date field, the function returns null for every row instead of the formatted string. This behavior used to work in 2.17.
How can one reproduce the bug?
curl -X POST "localhost:9200/plugins/sql" \
-H 'Content-Type: application/json' \
-d '{"query": "SELECT date_format(birthdate, '"'"'yyyy-MM'"'"') AS ym FROM accounts LIMIT 5"}'
Expected: a ym column with values like 1990-03. Actual: a ym column with null for every row. No error, no stack trace.
What is your host/environment?
OS: Amazon Linux 2
OpenSearch version: 2.19.0
SQL plugin version: 2.19.0 (bundled)
Do you have any screenshots?
N/A
Do you have any additional context?
Worked in 2.17. Regression introduced somewhere between 2.17 and 2.19.
[RCA] date_format returns null because the sample accounts dataset has no birthdate field, not a regression in the function.
An all-null output column from date_format(birthdate, 'yyyy-MM') with no error or stack trace is consistent with nullMissingHandling short-circuiting on a missing/null input on every row. Code-reading across 2.17 and 2.19 shows date_format() is byte-identical between the two branches — there is no regression in the function — and the bundled accounts dataset used as the default sample has no birthdate field.
All date_format overloads are wrapped with nullMissingHandling. That wrapper returns null/missing for any row where either input (the date expression or the format string) is null or missing, before the formatting code runs. An invalid format string would instead raise an error, and a type mismatch would fail function resolution — neither matches the reported "null for every row, no error." The only code path that produces silent null-per-row output is a null/missing input value on every row.
The bundled accounts dataset that ships with this repo does not define a birthdate field. Indexing it as-is produces documents with fields account_number, balance, firstname, lastname, age, gender, address, employer, email, city, state only. Querying birthdate against such an index resolves to missing on every document, which is exactly the input condition that makes nullMissingHandling emit null for every row.
The "worked in 2.17, broken in 2.19" framing is not supported by the code: between upstream/2.17 @ 43738d1aa and upstream/2.19 @ 5b5dfee4d, the date_format() resolver definition is byte-for-byte identical. The only commit between those refs touching DateTimeFunction(s).java is a str_to_date two-digit year fix, which does not affect date_format.
Evidence
Function registration and null-handling wrapper (read on upstream/main @ d3bdca82a):
// DateTimeFunctions.java L1106-L1124privateDefaultFunctionResolverdate_format() {
returndefine(
BuiltinFunctionName.DATE_FORMAT.getName(),
impl(nullMissingHandling(DateTimeFormatterUtil::getFormattedDate), STRING, STRING, STRING),
impl(nullMissingHandling(DateTimeFormatterUtil::getFormattedDate), STRING, DATE, STRING),
// ... TIME and TIMESTAMP overloads, all wrapped with nullMissingHandling
);
}
Regression check — date_format() is unchanged between 2.17 and 2.19:
$ git log --oneline upstream/2.17..upstream/2.19 -- \
core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java \
core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFormatterUtil.java
f412de04c [Backport 2.x] Function `str_to_date` should work with two-digits year (#3227)
Inspecting f412de04c shows it modifies DateTimeFormatterUtil.java for str_to_date only; date_format() resolver bodies on both branches are identical at line 1253 of DateTimeFunction.java.
Sample data inspection — no birthdate in accounts.json:
Live verification: not performed. Two converging code signals (no regression in date_format between 2.17 and 2.19; nullMissingHandling is the only all-null-per-row path) plus confirmation that the canonical sample dataset lacks birthdate make running a local cluster unnecessary.
Confidence: High
Three independent signals agree: (1) the date_format resolver is byte-identical between upstream/2.17 and upstream/2.19 — no regression is possible in this function; (2) nullMissingHandling is the only code path in the function that can produce null-per-row with no error; (3) the bundled accounts dataset used in the reproducer has no birthdate field, which exactly triggers that path.
Next steps
Confirm the index mapping and the actual documents in the reporter's cluster:
If birthdate is absent or present-but-null on every document, that confirms the hypothesis. If birthdate exists and is populated with a date type and date_format still returns null, this is a different issue and should be re-opened with the mapping and a sample document.
Unverified suggestion: if the user's index is meant to have birthdate but does not, either re-index with the field or point the query at a dataset that has it (e.g., a custom index, not the canonical accounts fixture).
Unverified suggestion: if the user was following a blog/tutorial that assumed birthdate existed in the sample dataset, the tutorial is out of date — accounts.json in this repo has never included birthdate in the checked-out range.
If the mapping confirms birthdate is a populated date field and the query still returns all null, escalate with /reproduce so the behavior can be verified against a seeded index locally.
Activity
RyanL1997 commentedon May 4, 2026
@ryan-gh-bot /rca
ryan-gh-bot commentedon May 4, 2026
[Working] on
/rca(invoked by @RyanL1997). I'll post the result when done. This usually takes 30 seconds to 5 minutes depending on the command.ryan-gh-bot commentedon May 4, 2026
[RCA]
date_formatreturns null because the sampleaccountsdataset has nobirthdatefield, not a regression in the function.An all-null output column from
date_format(birthdate, 'yyyy-MM')with no error or stack trace is consistent withnullMissingHandlingshort-circuiting on a missing/null input on every row. Code-reading across 2.17 and 2.19 showsdate_format()is byte-identical between the two branches — there is no regression in the function — and the bundledaccountsdataset used as the default sample has nobirthdatefield.Environment
sql(opensearch-project/sql)upstream/main @ d3bdca82aupstream/2.19 @ 5b5dfee4dupstream/2.17 @ 43738d1aaSuspected location
core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunctions.javaL1106-L1124 —date_formatregistration; every signature routes throughDateTimeFormatterUtil::getFormattedDatewrapped bynullMissingHandling.core/src/main/java/org/opensearch/sql/expression/function/FunctionDSL.javaL400-L410 — binarynullMissingHandling: returnsmissingValue()/nullValue()before invoking the implementation when either input is missing/null.integ-test/src/test/resources/accounts.json— has nobirthdatefield on any document.Analysis
All
date_formatoverloads are wrapped withnullMissingHandling. That wrapper returnsnull/missingfor any row where either input (the date expression or the format string) is null or missing, before the formatting code runs. An invalid format string would instead raise an error, and a type mismatch would fail function resolution — neither matches the reported "null for every row, no error." The only code path that produces silent null-per-row output is a null/missing input value on every row.The bundled
accountsdataset that ships with this repo does not define abirthdatefield. Indexing it as-is produces documents with fieldsaccount_number,balance,firstname,lastname,age,gender,address,employer,email,city,stateonly. Queryingbirthdateagainst such an index resolves to missing on every document, which is exactly the input condition that makesnullMissingHandlingemit null for every row.The "worked in 2.17, broken in 2.19" framing is not supported by the code: between
upstream/2.17 @ 43738d1aaandupstream/2.19 @ 5b5dfee4d, thedate_format()resolver definition is byte-for-byte identical. The only commit between those refs touchingDateTimeFunction(s).javais astr_to_datetwo-digit year fix, which does not affectdate_format.Evidence
Function registration and null-handling wrapper (read on
upstream/main @ d3bdca82a):Regression check —
date_format()is unchanged between 2.17 and 2.19:$ git log --oneline upstream/2.17..upstream/2.19 -- \ core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java \ core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFormatterUtil.java f412de04c [Backport 2.x] Function `str_to_date` should work with two-digits year (#3227)Inspecting
f412de04cshows it modifiesDateTimeFormatterUtil.javaforstr_to_dateonly;date_format()resolver bodies on both branches are identical at line 1253 ofDateTimeFunction.java.Sample data inspection — no
birthdateinaccounts.json:$ head -2 integ-test/src/test/resources/accounts.json {"index":{"_id":"1"}} {"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"} $ grep -c 'birthdate' integ-test/src/test/resources/accounts.json 0Live verification: not performed. Two converging code signals (no regression in
date_formatbetween 2.17 and 2.19;nullMissingHandlingis the only all-null-per-row path) plus confirmation that the canonical sample dataset lacksbirthdatemake running a local cluster unnecessary.Confidence: High
Three independent signals agree: (1) the
date_formatresolver is byte-identical betweenupstream/2.17andupstream/2.19— no regression is possible in this function; (2)nullMissingHandlingis the only code path in the function that can produce null-per-row with no error; (3) the bundledaccountsdataset used in the reproducer has nobirthdatefield, which exactly triggers that path.Next steps
birthdateis absent or present-but-null on every document, that confirms the hypothesis. Ifbirthdateexists and is populated with a date type anddate_formatstill returns null, this is a different issue and should be re-opened with the mapping and a sample document.birthdatebut does not, either re-index with the field or point the query at a dataset that has it (e.g., a custom index, not the canonicalaccountsfixture).birthdateexisted in the sample dataset, the tutorial is out of date —accounts.jsonin this repo has never includedbirthdatein the checked-out range.birthdateis a populated date field and the query still returns all null, escalate with/reproduceso the behavior can be verified against a seeded index locally.Automated response from @ryan-gh-bot (maintainer-triggered agent). About this agent.