From 81146a3c81d66ff785c689ef7bbe3552888d0103 Mon Sep 17 00:00:00 2001 From: Marcin Romaszewicz Date: Fri, 19 Jun 2026 23:05:02 -0700 Subject: [PATCH] Add missing required parameter detection Closes: #134 The exploded form path bound struct-typed query params (e.g. types.Date, time.Time) via bindParamsToExplodedObject and returned nil when no fields were present, dropping the required check that the slice and primitive cases already perform. A missing required date param therefore passed silently instead of returning a RequiredParameterError. Honor required in the reflect.Struct case of both BindQueryParameterWithOptions and BindRawQueryParameter, and add regression tests for the absent-required case. Co-Authored-By: Claude Opus 4.8 (1M context) --- bindparam.go | 14 +++++++++++++- bindparam_test.go | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/bindparam.go b/bindparam.go index 1fccc87..da3081d 100644 --- a/bindparam.go +++ b/bindparam.go @@ -475,8 +475,14 @@ func BindQueryParameterWithOptions(style string, explode bool, required bool, pa var fieldsPresent bool fieldsPresent, err = bindParamsToExplodedObject(paramName, queryParams, output) // If no fields were set, and there is no error, we will not fall - // through to assign the destination. + // through to assign the destination. An absent required + // parameter is an error, matching the slice and primitive cases + // above; this also covers scalar struct types such as + // types.Date and time.Time. if !fieldsPresent { + if required { + return &RequiredParameterError{ParamName: paramName} + } return nil } default: @@ -707,7 +713,13 @@ func BindRawQueryParameter(style string, explode bool, required bool, paramName case reflect.Struct: var fieldsPresent bool fieldsPresent, err = bindParamsToExplodedObject(paramName, queryParams, output) + // An absent required parameter is an error, matching the slice + // and primitive cases above; this also covers scalar struct + // types such as types.Date and time.Time. if !fieldsPresent { + if required { + return &RequiredParameterError{ParamName: paramName} + } return nil } default: diff --git a/bindparam_test.go b/bindparam_test.go index f05d9e7..91dd150 100644 --- a/bindparam_test.go +++ b/bindparam_test.go @@ -544,6 +544,26 @@ func TestBindQueryParameter(t *testing.T) { assert.Nil(t, date) }) + // Regression test for https://github.com/oapi-codegen/runtime/issues/134: + // an absent *required* date (a struct-typed param) must report a + // RequiredParameterError, not pass silently. The exploded form path + // previously returned nil here, dropping the required check. + t.Run("date_form_explode_required_missing", func(t *testing.T) { + var date types.Date + queryParams := url.Values{} + err := BindQueryParameter("form", true, true, "date", queryParams, &date) + var requiredErr *RequiredParameterError + assert.ErrorAs(t, err, &requiredErr) + }) + + t.Run("date_form_no_explode_required_missing", func(t *testing.T) { + var date types.Date + queryParams := url.Values{} + err := BindQueryParameter("form", false, true, "date", queryParams, &date) + var requiredErr *RequiredParameterError + assert.ErrorAs(t, err, &requiredErr) + }) + t.Run("date_form_no_explode_required", func(t *testing.T) { expectedDate := types.Date{Time: time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)} var date types.Date