This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 5fc19f5dd4b0 CAMEL-17598: camel-bindy: continue unmarshal on per-field 
parse failure (#23922)
5fc19f5dd4b0 is described below

commit 5fc19f5dd4b0a9070e53ce83a823673d54d80bea
Author: Vishal Nagaraj <[email protected]>
AuthorDate: Sat Jun 13 12:05:21 2026 +0530

    CAMEL-17598: camel-bindy: continue unmarshal on per-field parse failure 
(#23922)
    
    Today a single malformed value (a bad date, a non-numeric int, etc.)
    aborts the entire Bindy unmarshal even when the rest of the row would
    have parsed fine. Add an opt-in tolerance mode that catches per-field
    parse failures and substitutes a fallback so processing continues.
    
    Two new annotation elements drive it: continueParseOnFailure on
    @CsvRecord, @FixedLengthRecord, @Message (record-level boolean, default
    false) and continueParseOnFailure on @DataField, @KeyValuePairField
    (field-level tri-state ResumeUnmarshalingState, default INHERIT). The
    field value can override the record default; INHERIT defers to the
    record. All defaults preserve today's strict behaviour.
    
    When tolerant and parsing fails, Bindy substitutes @DataField.defaultValue
    parsed through the same Format, so the substitute is automatically the
    right type. With no defaultValue, the field gets the existing
    getDefaultValueForPrimitive convention: null for objects, "" for String,
    false for boolean, MIN_VALUE for numeric primitives.
    @KeyValuePairField has no defaultValue today, so KVP fields only get
    the primitive/null fallback.
    
    A pluggable ParserErrorHandler interface was discussed and intentionally
    deferred to keep this PR small; can be added later as a non-breaking
    addition if needed.
    
    Tests cover the full override matrix and value-substitution paths
    across all three factories. Full camel-bindy suite passes.
---
 .../src/main/docs/bindy-dataformat.adoc            | 100 ++++++
 .../dataformat/bindy/BindyAbstractFactory.java     |  35 ++
 .../camel/dataformat/bindy/BindyCsvFactory.java    |  13 +-
 .../dataformat/bindy/BindyFixedLengthFactory.java  |   7 +-
 .../dataformat/bindy/BindyKeyValuePairFactory.java |  22 +-
 .../bindy/annotation/ContinueOnFailure.java        |  24 ++
 .../dataformat/bindy/annotation/CsvRecord.java     |   7 +
 .../dataformat/bindy/annotation/DataField.java     |  10 +
 .../bindy/annotation/FixedLengthRecord.java        |   7 +
 .../bindy/annotation/KeyValuePairField.java        |  10 +
 .../camel/dataformat/bindy/annotation/Message.java |   7 +
 .../csv2/BindyCsvContinueOnParseFailureTest.java   | 376 +++++++++++++++++++++
 .../fix/BindyKvpContinueOnParseFailureTest.java    | 237 +++++++++++++
 .../BindyFixedContinueOnParseFailureTest.java      | 303 +++++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_21.adoc    |  36 ++
 15 files changed, 1186 insertions(+), 8 deletions(-)

diff --git a/components/camel-bindy/src/main/docs/bindy-dataformat.adoc 
b/components/camel-bindy/src/main/docs/bindy-dataformat.adoc
index 080c071b9739..95c9a813401c 100644
--- a/components/camel-bindy/src/main/docs/bindy-dataformat.adoc
+++ b/components/camel-bindy/src/main/docs/bindy-dataformat.adoc
@@ -112,6 +112,10 @@ expressions, e.g., the '\|' sign, then you have to mask 
it, like '\|'
 | autospanLine | boolean |  | false a| Last record spans rest of line 
(optional) - if enabled then the last column is auto spanned to end of line, for
 example if it is a comment, etc this allows the line to contain all 
characters, also the delimiter char.
 
+| continueParseOnFailure | boolean |  | false a| If true, a parse failure on 
any field in this record is replaced with the field's defaultValue (or the
+type-appropriate default if no defaultValue is set), instead of aborting the 
unmarshal. Individual fields can opt
+out per-field via @DataField.continueParseOnFailure. Default false preserves 
the existing fail-fast behavior.
+
 | crlf | String |  | WINDOWS a| Character to be used to add a carriage return 
after each record (optional) - allow defining the carriage return
 character to use. If you specify a value other than the three listed before, 
the value you enter (custom) will be
 used as the CRLF character(s). Three values can be used : WINDOWS, UNIX, MAC, 
or custom.
@@ -430,6 +434,13 @@ int, date, ...) and optionally of a pattern.
 | columnName | String |  |  a| Name of the header column (optional). Uses the 
name of the property as default. Only applicable when `CsvRecord`
 has `generateHeaderColumns = true`
 
+| continueParseOnFailure | ContinueOnFailure |  | INHERIT a| Whether to keep 
going when parsing this field fails.
+
+TRUE forces tolerance for this field — a parse error is replaced with the 
field's defaultValue, or the
+type-appropriate default if no defaultValue is set. FALSE forces strict 
behavior: the exception propagates and
+aborts the unmarshal as it always has. INHERIT (the default) defers to the 
record-level setting on @CsvRecord
+or @FixedLengthRecord.
+
 | decimalSeparator | String |  |  | Decimal Separator to be used with 
BigDecimal number
 
 | defaultValue | String |  |  | Field's default value in case no value is set
@@ -759,6 +770,10 @@ field, we can then add 'pad' characters.
 |===
 | Parameter name | Type | Required | Default value | Info
 
+| continueParseOnFailure | boolean |  | false a| If true, a parse failure on 
any field in this record is replaced with the field's defaultValue (or the
+type-appropriate default if no defaultValue is set), instead of aborting the 
unmarshal. Individual fields can opt
+out per-field via @DataField.continueParseOnFailure. Default false preserves 
the existing fail-fast behavior.
+
 | countGrapheme | boolean |  | false | Indicates how chars are counted
 
 | crlf | String |  | WINDOWS a| Character to be used to add a carriage return 
after each record (optional). Possible values: WINDOWS, UNIX, MAC,
@@ -1213,6 +1228,10 @@ or 'anything'.
 
 | pairSeparator | String | &#10003; |  | Pair separator used to split the key 
value pairs in tokens (mandatory). Can be '=', ';', or 'anything'.
 
+| continueParseOnFailure | boolean |  | false a| If true, a parse failure on 
any field in this message is replaced with the type-appropriate default (null, 
"",
+false, or MIN_VALUE for numeric primitives), instead of aborting the 
unmarshal. Individual fields can opt out
+per-field via @KeyValuePairField.continueParseOnFailure. Default false 
preserves the existing fail-fast behavior.
+
 | crlf | String |  | WINDOWS a| Character to be used to add a carriage return 
after each record (optional). Possible values = WINDOWS, UNIX, MAC,
 or custom. If you specify a value other than the three listed before, the 
value you enter (custom) will be used
 as the CRLF character(s).
@@ -1282,6 +1301,13 @@ pattern and if the field is required.
 
 | tag | int | &#10003; |  | tag identifying the field in the message 
(mandatory) - must be unique
 
+| continueParseOnFailure | ContinueOnFailure |  | INHERIT a| Whether to keep 
going when parsing this field fails.
+
+TRUE forces tolerance — a parse error is replaced with the type-appropriate 
default (null, "", false, or
+MIN_VALUE for numeric primitives). KeyValuePairField doesn't currently have a 
defaultValue element, so there's no
+user-supplied substitute available here. FALSE forces strict behavior. INHERIT 
(the default) defers
+to @Message.continueParseOnFailure.
+
 | impliedDecimalSeparator | boolean |  | false | <b>Camel 2.11:</b> Indicates 
if there is a decimal point implied at a specified location
 
 | name | String |  |  | name of the field (optional)
@@ -1668,6 +1694,80 @@ public static class OrderNumberFormatFactory extends 
AbstractFormatFactory {
 }
 ----
 
+=== Handling parse failures
+
+By default, when Bindy can't parse a field — say a malformed date, or `"abc"` 
in an `int` column — it
+throws and the whole unmarshal aborts, even if the rest of the row would have 
parsed fine. You can
+opt into a tolerant mode where the bad field is replaced with a fallback value 
and the row is delivered
+intact.
+
+==== Turning it on
+
+There are two annotation elements, on different levels:
+
+* `continueParseOnFailure` on the *record* annotation (`@CsvRecord`, 
`@FixedLengthRecord`, `@Message`)
+  — a plain boolean that sets the default for every field in that record.
+* `continueParseOnFailure` on the *field* annotation (`@DataField`, 
`@KeyValuePairField`) — a tri-state
+  enum (`ContinueOnFailure`) that can override the record default for one 
specific field.
+
+The tri-state lets a single strict field live inside an otherwise-tolerant 
record, or vice versa:
+
+[width="100%",cols="34%,33%,33%",options="header"]
+|===
+| `@CsvRecord` | `@DataField` | Effective behavior
+| (unset, `false`) | `INHERIT` (default) | Strict — exception propagates, like 
today
+| `true` | `INHERIT` | Tolerant
+| `true` | `FALSE` | Strict (the field overrides the record)
+| `false` | `TRUE` | Tolerant (the field overrides the record)
+|===
+
+==== What the tolerant path substitutes
+
+When a parse fails and the field is tolerant, Bindy picks a substitute value 
in this order:
+
+. If the field has a `@DataField.defaultValue` set, it's parsed through the 
*same* `Format` that just
+  failed. So a `defaultValue = "1970-01-01"` on a `Date` field becomes a real 
`Date`, a
+  `defaultValue = "0.00"` on a `BigDecimal` becomes a real `BigDecimal`, and 
so on. The type is
+  always correct.
+. Otherwise the field gets the same fallback an unfilled field would get: 
`null` for object types
+  (`Date`, `BigDecimal`, `String` with `defaultValueStringAsNull=true`, custom 
converters), `""` for
+  `String`, `false` for `boolean`, and `MIN_VALUE` for numeric primitives 
(`int`, `long`, `byte`,
+  `short`, `float`, `double`, `char`).
+
+[NOTE]
+====
+The `MIN_VALUE` sentinel for numeric primitives is Bindy's existing convention 
from
+`getDefaultValueForPrimitive`; it's not specific to this feature. If you want 
`0` instead, set an
+explicit `defaultValue = "0"` on the field.
+====
+
+==== Example
+
+[source,java]
+----
+@CsvRecord(separator = ",", continueParseOnFailure = true)
+public class Order {
+    @DataField(pos = 1)
+    private int id;                       // bad int -> Integer.MIN_VALUE
+
+    @DataField(pos = 2, pattern = "yyyy-MM-dd", defaultValue = "1970-01-01")
+    private Date orderDate;               // bad date -> 1970-01-01
+
+    @DataField(pos = 3, continueParseOnFailure = ContinueOnFailure.FALSE)
+    private BigDecimal amount;            // bad number still throws; this 
field is exempt
+}
+----
+
+A row like `1,2026-01-15,42.50` parses normally. A row like 
`xyz,not-a-date,42.50` produces an `Order`
+with `id = Integer.MIN_VALUE`, `orderDate = 1970-01-01`, and `amount = 42.50`. 
A row with a bad
+`amount` (e.g. `1,2026-01-15,xyz`) still throws, because that field is 
annotated strict.
+
+==== KVP caveat
+
+`@KeyValuePairField` does not currently have a `defaultValue` element, so KVP 
fields can only fall
+back to the type-appropriate default (`null`, `""`, `false`, or `MIN_VALUE`). 
Everything else works
+the same as CSV and fixed-length.
+
 === Supported Datatypes
 
 The DefaultFormatFactory makes formatting of the following datatype available 
by
diff --git 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyAbstractFactory.java
 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyAbstractFactory.java
index add9f55cffe8..eea74829d8f5 100644
--- 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyAbstractFactory.java
+++ 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyAbstractFactory.java
@@ -28,6 +28,7 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.dataformat.bindy.annotation.ContinueOnFailure;
 import org.apache.camel.dataformat.bindy.annotation.Link;
 import org.apache.camel.dataformat.bindy.annotation.OneToMany;
 import org.apache.camel.support.ObjectHelper;
@@ -50,6 +51,7 @@ public abstract class BindyAbstractFactory implements 
BindyFactory {
     private String locale;
     private Class<?> type;
     private boolean defaultValueStringAsNull;
+    protected boolean continueParseOnFailure;
 
     protected BindyAbstractFactory(Class<?> type) throws Exception {
         this.type = type;
@@ -204,6 +206,39 @@ public abstract class BindyAbstractFactory implements 
BindyFactory {
         return Integer.valueOf(keyGenerated);
     }
 
+    protected boolean shouldContinueOnFailure(ContinueOnFailure fieldOpinion) {
+        return switch (fieldOpinion) {
+            case TRUE -> true;
+            case FALSE -> false;
+            case INHERIT -> continueParseOnFailure;
+        };
+    }
+
+    /**
+     * Parse a single field value with optional tolerance for failures.
+     *
+     * If parsing succeeds, returns the parsed value. If it fails and {@code 
continueOnFailure} is false, the original
+     * exception is rethrown so the caller can wrap it with position/line 
context (this is the existing fail-fast
+     * behavior). If it fails and {@code continueOnFailure} is true, returns 
the field's {@code defaultValue} parsed
+     * through the same Format, or — if no defaultValue is set — the 
type-appropriate default from
+     * {@link #getDefaultValueForPrimitive}.
+     */
+    protected Object parseField(
+            Format format, String string, boolean continueOnFailure, Class<?> 
fieldType, String defaultValue)
+            throws Exception {
+        try {
+            return format.parse(string);
+        } catch (Exception e) {
+            if (!continueOnFailure) {
+                throw e;
+            }
+            if (!defaultValue.isEmpty()) {
+                return format.parse(defaultValue);
+            }
+            return getDefaultValueForPrimitive(fieldType, 
isDefaultValueStringAsNull());
+        }
+    }
+
     private static NumberFormat getNumberFormat() {
         // Get instance of NumberFormat
         NumberFormat nf = NumberFormat.getInstance();
diff --git 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyCsvFactory.java
 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyCsvFactory.java
index 9c095e08402a..b9fcf9cbbf9a 100644
--- 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyCsvFactory.java
+++ 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyCsvFactory.java
@@ -215,6 +215,8 @@ public class BindyCsvFactory extends BindyAbstractFactory 
implements BindyFactor
             data = data.trim();
         }
 
+        boolean fieldContinueParseOnFailure = 
shouldContinueOnFailure(dataField.continueParseOnFailure());
+
         if (dataField.required()) {
             // Increment counter of mandatory fields
             ++counterMandatoryFields;
@@ -251,14 +253,16 @@ public class BindyCsvFactory extends BindyAbstractFactory 
implements BindyFactor
         if (!data.isEmpty()) {
             try {
                 if (quoting && quote != null && (data.contains("\\" + quote) 
|| data.contains(quote)) && quotingEscaped) {
-                    value = format.parse(data.replaceAll("\\\\" + quote, "\\" 
+ quote));
+                    value = parseField(format, data.replaceAll("\\\\" + quote, 
"\\" + quote), fieldContinueParseOnFailure,
+                            field.getType(), dataField.defaultValue());
                 } else if (quote != null && quote.equals(DOUBLE_QUOTES_SYMBOL)
                         && data.contains(DOUBLE_QUOTES_SYMBOL + 
DOUBLE_QUOTES_SYMBOL) && !quotingEscaped) {
                     // If double-quotes are used to enclose fields, the two 
double
                     // quotes character must be replaced with one according to 
RFC 4180 section 2.7
-                    value = format.parse(data.replace(DOUBLE_QUOTES_SYMBOL + 
DOUBLE_QUOTES_SYMBOL, DOUBLE_QUOTES_SYMBOL));
+                    value = parseField(format, 
data.replace(DOUBLE_QUOTES_SYMBOL + DOUBLE_QUOTES_SYMBOL, DOUBLE_QUOTES_SYMBOL),
+                            fieldContinueParseOnFailure, field.getType(), 
dataField.defaultValue());
                 } else {
-                    value = format.parse(data);
+                    value = parseField(format, data, 
fieldContinueParseOnFailure, field.getType(), dataField.defaultValue());
                 }
             } catch (FormatException ie) {
                 throw new IllegalArgumentException(ie.getMessage() + ", 
position: " + pos + ", line: " + line, ie);
@@ -693,6 +697,9 @@ public class BindyCsvFactory extends BindyAbstractFactory 
implements BindyFactor
 
                     trimLine = csvRecord.trimLine();
                     LOG.debug("Trim line: {}", trimLine);
+
+                    continueParseOnFailure = 
csvRecord.continueParseOnFailure();
+                    LOG.debug("Continue parse on failure: {}", 
continueParseOnFailure);
                 }
 
                 if (section != null) {
diff --git 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyFixedLengthFactory.java
 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyFixedLengthFactory.java
index 3a1934661d8c..ac772c6c34df 100644
--- 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyFixedLengthFactory.java
+++ 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyFixedLengthFactory.java
@@ -238,6 +238,8 @@ public class BindyFixedLengthFactory extends 
BindyAbstractFactory implements Bin
                 //token = token.trim();
             }
 
+            boolean fieldContinueParseOnFailure = 
shouldContinueOnFailure(dataField.continueParseOnFailure());
+
             // Check mandatory field
             if (dataField.required()) {
 
@@ -279,7 +281,7 @@ public class BindyFixedLengthFactory extends 
BindyAbstractFactory implements Bin
             }
             if (!token.isEmpty()) {
                 try {
-                    value = format.parse(token);
+                    value = parseField(format, token, 
fieldContinueParseOnFailure, field.getType(), dataField.defaultValue());
                 } catch (FormatException ie) {
                     throw new IllegalArgumentException(ie.getMessage() + ", 
position: " + offset + ", line: " + line, ie);
                 } catch (Exception e) {
@@ -623,6 +625,9 @@ public class BindyFixedLengthFactory extends 
BindyAbstractFactory implements Bin
 
                 countGrapheme = fixedLengthRecord.countGrapheme();
                 LOG.debug("Enable grapheme counting instead of codepoints: 
{}", countGrapheme);
+
+                continueParseOnFailure = 
fixedLengthRecord.continueParseOnFailure();
+                LOG.debug("Continue parse on failure: {}", 
continueParseOnFailure);
             }
         }
 
diff --git 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyKeyValuePairFactory.java
 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyKeyValuePairFactory.java
index 70d38f0eec27..84acd3dc28ea 100644
--- 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyKeyValuePairFactory.java
+++ 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyKeyValuePairFactory.java
@@ -293,8 +293,12 @@ public class BindyKeyValuePairFactory extends 
BindyAbstractFactory implements Bi
                                         getLocale());
                                 Format<?> format = 
formatFactory.getFormat(formattingOptions);
 
+                                boolean fieldContinueParseOnFailure
+                                        = 
shouldContinueOnFailure(keyValuePairField.continueParseOnFailure());
+
                                 // format the value of the key received
-                                result = formatField(format, value, key, line);
+                                result = formatField(format, value, key, line, 
fieldContinueParseOnFailure, field.getType(),
+                                        "");
 
                                 LOG.debug("Value formated : {}", result);
 
@@ -335,8 +339,12 @@ public class BindyKeyValuePairFactory extends 
BindyAbstractFactory implements Bi
                                             getLocale());
                                     Format<?> format = 
formatFactory.getFormat(formattingOptions);
 
+                                    boolean fieldContinueParseOnFailure
+                                            = 
shouldContinueOnFailure(keyValuePairField.continueParseOnFailure());
+
                                     // format the value of the key received
-                                    Object result = formatField(format, value, 
key, line);
+                                    Object result = formatField(format, value, 
key, line, fieldContinueParseOnFailure,
+                                            field.getType(), "");
 
                                     LOG.debug("Value formated : {}", result);
 
@@ -573,7 +581,10 @@ public class BindyKeyValuePairFactory extends 
BindyAbstractFactory implements Bi
         return builder.toString();
     }
 
-    private Object formatField(Format<?> format, String value, int tag, int 
line) throws Exception {
+    private Object formatField(
+            Format<?> format, String value, int tag, int line, boolean 
continueOnFailure, Class<?> fieldType,
+            String defaultValue)
+            throws Exception {
 
         Object obj = null;
 
@@ -581,7 +592,7 @@ public class BindyKeyValuePairFactory extends 
BindyAbstractFactory implements Bi
 
             // Format field value
             try {
-                obj = format.parse(value);
+                obj = parseField(format, value, continueOnFailure, fieldType, 
defaultValue);
             } catch (Exception e) {
                 throw new IllegalArgumentException(
                         "Parsing error detected for field defined at the tag: 
" + tag + ", line: " + line, e);
@@ -648,6 +659,9 @@ public class BindyKeyValuePairFactory extends 
BindyAbstractFactory implements Bi
                     // Get isOrdered parameter
                     messageOrdered = message.isOrdered();
                     LOG.debug("Is the message ordered in output: {}", 
messageOrdered);
+
+                    continueParseOnFailure = message.continueParseOnFailure();
+                    LOG.debug("Continue parse on failure: {}", 
continueParseOnFailure);
                 }
 
                 if (section != null) {
diff --git 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/ContinueOnFailure.java
 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/ContinueOnFailure.java
new file mode 100644
index 000000000000..2c92213f9c18
--- /dev/null
+++ 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/ContinueOnFailure.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dataformat.bindy.annotation;
+
+public enum ContinueOnFailure {
+
+    FALSE,
+    TRUE,
+    INHERIT
+}
diff --git 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/CsvRecord.java
 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/CsvRecord.java
index 5e1d461b5c5d..e2012370a7d2 100644
--- 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/CsvRecord.java
+++ 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/CsvRecord.java
@@ -124,4 +124,11 @@ public @interface CsvRecord {
      */
     boolean trimLine() default true;
 
+    /**
+     * If true, a parse failure on any field in this record is replaced with 
the field's defaultValue (or the
+     * type-appropriate default if no defaultValue is set), instead of 
aborting the unmarshal. Individual fields can opt
+     * out per-field via @DataField.continueParseOnFailure. Default false 
preserves the existing fail-fast behavior.
+     */
+    boolean continueParseOnFailure() default false;
+
 }
diff --git 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/DataField.java
 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/DataField.java
index 1b79525574a3..4ef891393cd3 100644
--- 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/DataField.java
+++ 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/DataField.java
@@ -152,4 +152,14 @@ public @interface DataField {
      * 
org.apache.camel.dataformat.bindy.csv.BindySimpleCsvFunctionWithExternalMethodTest.replaceToBar
      */
     String method() default "";
+
+    /**
+     * Whether to keep going when parsing this field fails.
+     *
+     * TRUE forces tolerance for this field — a parse error is replaced with 
the field's defaultValue, or the
+     * type-appropriate default if no defaultValue is set. FALSE forces strict 
behavior: the exception propagates and
+     * aborts the unmarshal as it always has. INHERIT (the default) defers to 
the record-level setting on @CsvRecord
+     * or @FixedLengthRecord.
+     */
+    ContinueOnFailure continueParseOnFailure() default 
ContinueOnFailure.INHERIT;
 }
diff --git 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/FixedLengthRecord.java
 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/FixedLengthRecord.java
index b54e3317b302..94bd5af9ef23 100644
--- 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/FixedLengthRecord.java
+++ 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/FixedLengthRecord.java
@@ -107,4 +107,11 @@ public @interface FixedLengthRecord {
      * Indicates how chars are counted
      */
     boolean countGrapheme() default false;
+
+    /**
+     * If true, a parse failure on any field in this record is replaced with 
the field's defaultValue (or the
+     * type-appropriate default if no defaultValue is set), instead of 
aborting the unmarshal. Individual fields can opt
+     * out per-field via @DataField.continueParseOnFailure. Default false 
preserves the existing fail-fast behavior.
+     */
+    boolean continueParseOnFailure() default false;
 }
diff --git 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/KeyValuePairField.java
 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/KeyValuePairField.java
index 88863bde0b62..87f2fc2cab26 100644
--- 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/KeyValuePairField.java
+++ 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/KeyValuePairField.java
@@ -85,4 +85,14 @@ public @interface KeyValuePairField {
      * <b>Camel 2.11:</b> Indicates if there is a decimal point implied at a 
specified location
      */
     boolean impliedDecimalSeparator() default false;
+
+    /**
+     * Whether to keep going when parsing this field fails.
+     *
+     * TRUE forces tolerance — a parse error is replaced with the 
type-appropriate default (null, "", false, or
+     * MIN_VALUE for numeric primitives). KeyValuePairField doesn't currently 
have a defaultValue element, so there's no
+     * user-supplied substitute available here. FALSE forces strict behavior. 
INHERIT (the default) defers
+     * to @Message.continueParseOnFailure.
+     */
+    ContinueOnFailure continueParseOnFailure() default 
ContinueOnFailure.INHERIT;
 }
diff --git 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/Message.java
 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/Message.java
index d2aab5eb50fe..a318674c268d 100644
--- 
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/Message.java
+++ 
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/Message.java
@@ -82,4 +82,11 @@ public @interface Message {
      * @return boolean
      */
     boolean isOrdered() default false;
+
+    /**
+     * If true, a parse failure on any field in this message is replaced with 
the type-appropriate default (null, "",
+     * false, or MIN_VALUE for numeric primitives), instead of aborting the 
unmarshal. Individual fields can opt out
+     * per-field via @KeyValuePairField.continueParseOnFailure. Default false 
preserves the existing fail-fast behavior.
+     */
+    boolean continueParseOnFailure() default false;
 }
diff --git 
a/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/csv2/BindyCsvContinueOnParseFailureTest.java
 
b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/csv2/BindyCsvContinueOnParseFailureTest.java
new file mode 100644
index 000000000000..54f5b4d3f6e2
--- /dev/null
+++ 
b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/csv2/BindyCsvContinueOnParseFailureTest.java
@@ -0,0 +1,376 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dataformat.bindy.csv2;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.dataformat.bindy.annotation.ContinueOnFailure;
+import org.apache.camel.dataformat.bindy.annotation.CsvRecord;
+import org.apache.camel.dataformat.bindy.annotation.DataField;
+import org.apache.camel.dataformat.bindy.csv.BindyCsvDataFormat;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Verifies the continueParseOnFailure / continueParseOnFailure override 
matrix: record-level boolean on @CsvRecord
+ * interacts with field-level tri-state on @DataField so that the field 
opinion overrides the record default, and
+ * INHERIT falls back to the record.
+ *
+ * Each model has three fields (id, orderDate, customerName); the bad row 
supplies a malformed date string at position
+ * 2. Tolerant outcome: row is delivered, orderDate is null, and customerName 
is populated (proving the parser continued
+ * past the bad field). Strict outcome: an exception propagates.
+ */
+public class BindyCsvContinueOnParseFailureTest extends CamelTestSupport {
+
+    private static final String BAD_ROW = "1,not-a-date,Alice";
+    private static final String GOOD_ROW = "1,2026-01-15,Alice";
+
+    @Test
+    public void recordUnset_fieldInherit_isStrict() {
+        assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:strict-default", BAD_ROW));
+    }
+
+    @Test
+    public void recordTolerant_fieldInherit_keepsRowWithNullField() throws 
Exception {
+        MockEndpoint mock = getMockEndpoint("mock:record-tolerant");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:record-tolerant", BAD_ROW);
+        MockEndpoint.assertIsSatisfied(context);
+
+        RecordTolerantOrder row = 
mock.getReceivedExchanges().get(0).getIn().getBody(RecordTolerantOrder.class);
+        assertNotNull(row);
+        assertEquals(1, row.id);
+        assertNull(row.orderDate);
+        assertEquals("Alice", row.customerName);
+    }
+
+    @Test
+    public void recordStrict_fieldInherit_isStrict() {
+        assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:record-strict", BAD_ROW));
+    }
+
+    @Test
+    public void recordTolerant_fieldFalse_fieldOverridesToStrict() {
+        assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:field-strict-override", 
BAD_ROW));
+    }
+
+    @Test
+    public void recordStrict_fieldTrue_fieldOverridesToTolerant() throws 
Exception {
+        MockEndpoint mock = getMockEndpoint("mock:field-tolerant-override");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:field-tolerant-override", BAD_ROW);
+        MockEndpoint.assertIsSatisfied(context);
+
+        FieldTolerantOverrideOrder row
+                = 
mock.getReceivedExchanges().get(0).getIn().getBody(FieldTolerantOverrideOrder.class);
+        assertNotNull(row);
+        assertEquals(1, row.id);
+        assertNull(row.orderDate);
+        assertEquals("Alice", row.customerName);
+    }
+
+    @Test
+    public void recordUnset_fieldTrue_keepsRowWithNullField() throws Exception 
{
+        MockEndpoint mock = getMockEndpoint("mock:field-tolerant");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:field-tolerant", BAD_ROW);
+        MockEndpoint.assertIsSatisfied(context);
+
+        FieldTolerantOrder row = 
mock.getReceivedExchanges().get(0).getIn().getBody(FieldTolerantOrder.class);
+        assertNotNull(row);
+        assertEquals(1, row.id);
+        assertNull(row.orderDate);
+        assertEquals("Alice", row.customerName);
+    }
+
+    // ---- defaultValue substitution ----
+
+    @Test
+    public void tolerant_withDefaultValue_substitutesParsedDefault() throws 
Exception {
+        // Bad date input + defaultValue set -> field is populated with the 
parsed defaultValue (not null)
+        MockEndpoint mock = getMockEndpoint("mock:tolerant-with-default");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:tolerant-with-default", BAD_ROW);
+        MockEndpoint.assertIsSatisfied(context);
+
+        TolerantWithDefaultValueOrder row
+                = 
mock.getReceivedExchanges().get(0).getIn().getBody(TolerantWithDefaultValueOrder.class);
+        assertNotNull(row);
+        assertEquals(1, row.id);
+        // Substituted from defaultValue="1970-01-01"
+        assertNotNull(row.orderDate);
+        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
+        assertEquals(fmt.parse("1970-01-01"), row.orderDate);
+        assertEquals("Alice", row.customerName);
+    }
+
+    @Test
+    public void strict_withDefaultValue_stillThrows() {
+        // Even if defaultValue is set, strict mode propagates the parse 
exception
+        assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:strict-with-default", 
BAD_ROW));
+    }
+
+    @Test
+    public void tolerant_malformedDefaultValue_throws() {
+        // defaultValue itself cannot be parsed by the field's format -> 
exception propagates
+        assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:tolerant-malformed-default", 
BAD_ROW));
+    }
+
+    // ---- primitive / null fallback when no defaultValue is set ----
+
+    @Test
+    public void tolerant_primitiveIntField_getsZero() throws Exception {
+        // Bad int input + tolerant + no defaultValue -> primitive default (0)
+        MockEndpoint mock = getMockEndpoint("mock:tolerant-primitive-int");
+        mock.expectedMessageCount(1);
+        // id at pos 1 is the bad field here; the date is valid
+        template.sendBody("direct:tolerant-primitive-int", 
"abc,2026-01-15,Alice");
+        MockEndpoint.assertIsSatisfied(context);
+
+        TolerantPrimitiveIntOrder row
+                = 
mock.getReceivedExchanges().get(0).getIn().getBody(TolerantPrimitiveIntOrder.class);
+        assertNotNull(row);
+        assertEquals(Integer.MIN_VALUE, row.id);   // Bindy's primitive 
default convention                  // primitive int default
+        assertNotNull(row.orderDate);             // good input still parses
+        assertEquals("Alice", row.customerName);
+    }
+
+    @Test
+    public void tolerant_multipleBadFields_eachGetsItsOwnFallback() throws 
Exception {
+        // Two bad fields on one row:
+        //   pos 1 (int) - no defaultValue -> 0
+        //   pos 2 (Date) - defaultValue="1970-01-01" -> parsed default
+        // pos 3 (String) is good -> "Alice"
+        MockEndpoint mock = getMockEndpoint("mock:tolerant-multi-bad");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:tolerant-multi-bad", "abc,not-a-date,Alice");
+        MockEndpoint.assertIsSatisfied(context);
+
+        TolerantMultiBadOrder row = 
mock.getReceivedExchanges().get(0).getIn().getBody(TolerantMultiBadOrder.class);
+        assertNotNull(row);
+        assertEquals(Integer.MIN_VALUE, row.id);   // Bindy's primitive 
default convention
+        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
+        assertEquals(fmt.parse("1970-01-01"), row.orderDate);
+        assertEquals("Alice", row.customerName);
+    }
+
+    @Test
+    public void tolerant_emptyDefaultValueExplicit_fallsThroughToPrimitive() 
throws Exception {
+        // defaultValue="" (explicitly empty) is treated the same as unset -> 
primitive default
+        MockEndpoint mock = getMockEndpoint("mock:tolerant-empty-default");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:tolerant-empty-default", 
"abc,2026-01-15,Alice");
+        MockEndpoint.assertIsSatisfied(context);
+
+        TolerantEmptyDefaultOrder row
+                = 
mock.getReceivedExchanges().get(0).getIn().getBody(TolerantEmptyDefaultOrder.class);
+        assertEquals(Integer.MIN_VALUE, row.id);   // Bindy's primitive 
default convention
+    }
+
+    @Test
+    public void recordTolerant_goodInput_parsesNormally() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:record-tolerant");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:record-tolerant", GOOD_ROW);
+        MockEndpoint.assertIsSatisfied(context);
+
+        RecordTolerantOrder row = 
mock.getReceivedExchanges().get(0).getIn().getBody(RecordTolerantOrder.class);
+        assertNotNull(row);
+        assertEquals(1, row.id);
+        assertNotNull(row.orderDate);
+        assertEquals("Alice", row.customerName);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:strict-default")
+                        .unmarshal(new 
BindyCsvDataFormat(StrictDefaultOrder.class))
+                        .to("mock:strict-default");
+                from("direct:record-tolerant")
+                        .unmarshal(new 
BindyCsvDataFormat(RecordTolerantOrder.class))
+                        .to("mock:record-tolerant");
+                from("direct:record-strict")
+                        .unmarshal(new 
BindyCsvDataFormat(RecordStrictOrder.class))
+                        .to("mock:record-strict");
+                from("direct:field-strict-override")
+                        .unmarshal(new 
BindyCsvDataFormat(FieldStrictOverrideOrder.class))
+                        .to("mock:field-strict-override");
+                from("direct:field-tolerant-override")
+                        .unmarshal(new 
BindyCsvDataFormat(FieldTolerantOverrideOrder.class))
+                        .to("mock:field-tolerant-override");
+                from("direct:field-tolerant")
+                        .unmarshal(new 
BindyCsvDataFormat(FieldTolerantOrder.class))
+                        .to("mock:field-tolerant");
+                from("direct:tolerant-with-default")
+                        .unmarshal(new 
BindyCsvDataFormat(TolerantWithDefaultValueOrder.class))
+                        .to("mock:tolerant-with-default");
+                from("direct:strict-with-default")
+                        .unmarshal(new 
BindyCsvDataFormat(StrictWithDefaultValueOrder.class))
+                        .to("mock:strict-with-default");
+                from("direct:tolerant-malformed-default")
+                        .unmarshal(new 
BindyCsvDataFormat(TolerantMalformedDefaultOrder.class))
+                        .to("mock:tolerant-malformed-default");
+                from("direct:tolerant-primitive-int")
+                        .unmarshal(new 
BindyCsvDataFormat(TolerantPrimitiveIntOrder.class))
+                        .to("mock:tolerant-primitive-int");
+                from("direct:tolerant-multi-bad")
+                        .unmarshal(new 
BindyCsvDataFormat(TolerantMultiBadOrder.class))
+                        .to("mock:tolerant-multi-bad");
+                from("direct:tolerant-empty-default")
+                        .unmarshal(new 
BindyCsvDataFormat(TolerantEmptyDefaultOrder.class))
+                        .to("mock:tolerant-empty-default");
+            }
+        };
+    }
+
+    @CsvRecord(separator = ",")
+    public static class StrictDefaultOrder {
+        @DataField(pos = 1)
+        public int id;
+        @DataField(pos = 2, pattern = "yyyy-MM-dd")
+        public Date orderDate;
+        @DataField(pos = 3)
+        public String customerName;
+    }
+
+    @CsvRecord(separator = ",", continueParseOnFailure = true)
+    public static class RecordTolerantOrder {
+        @DataField(pos = 1)
+        public int id;
+        @DataField(pos = 2, pattern = "yyyy-MM-dd")
+        public Date orderDate;
+        @DataField(pos = 3)
+        public String customerName;
+    }
+
+    @CsvRecord(separator = ",", continueParseOnFailure = false)
+    public static class RecordStrictOrder {
+        @DataField(pos = 1)
+        public int id;
+        @DataField(pos = 2, pattern = "yyyy-MM-dd")
+        public Date orderDate;
+        @DataField(pos = 3)
+        public String customerName;
+    }
+
+    @CsvRecord(separator = ",", continueParseOnFailure = true)
+    public static class FieldStrictOverrideOrder {
+        @DataField(pos = 1)
+        public int id;
+        @DataField(pos = 2, pattern = "yyyy-MM-dd", continueParseOnFailure = 
ContinueOnFailure.FALSE)
+        public Date orderDate;
+        @DataField(pos = 3)
+        public String customerName;
+    }
+
+    @CsvRecord(separator = ",", continueParseOnFailure = false)
+    public static class FieldTolerantOverrideOrder {
+        @DataField(pos = 1)
+        public int id;
+        @DataField(pos = 2, pattern = "yyyy-MM-dd", continueParseOnFailure = 
ContinueOnFailure.TRUE)
+        public Date orderDate;
+        @DataField(pos = 3)
+        public String customerName;
+    }
+
+    @CsvRecord(separator = ",")
+    public static class FieldTolerantOrder {
+        @DataField(pos = 1)
+        public int id;
+        @DataField(pos = 2, pattern = "yyyy-MM-dd", continueParseOnFailure = 
ContinueOnFailure.TRUE)
+        public Date orderDate;
+        @DataField(pos = 3)
+        public String customerName;
+    }
+
+    @CsvRecord(separator = ",", continueParseOnFailure = true)
+    public static class TolerantWithDefaultValueOrder {
+        @DataField(pos = 1)
+        public int id;
+        @DataField(pos = 2, pattern = "yyyy-MM-dd", defaultValue = 
"1970-01-01")
+        public Date orderDate;
+        @DataField(pos = 3)
+        public String customerName;
+    }
+
+    @CsvRecord(separator = ",")
+    public static class StrictWithDefaultValueOrder {
+        @DataField(pos = 1)
+        public int id;
+        @DataField(pos = 2, pattern = "yyyy-MM-dd", defaultValue = 
"1970-01-01")
+        public Date orderDate;
+        @DataField(pos = 3)
+        public String customerName;
+    }
+
+    @CsvRecord(separator = ",", continueParseOnFailure = true)
+    public static class TolerantMalformedDefaultOrder {
+        @DataField(pos = 1)
+        public int id;
+        @DataField(pos = 2, pattern = "yyyy-MM-dd", defaultValue = 
"this-is-not-a-date")
+        public Date orderDate;
+        @DataField(pos = 3)
+        public String customerName;
+    }
+
+    @CsvRecord(separator = ",", continueParseOnFailure = true)
+    public static class TolerantPrimitiveIntOrder {
+        @DataField(pos = 1)
+        public int id;
+        @DataField(pos = 2, pattern = "yyyy-MM-dd")
+        public Date orderDate;
+        @DataField(pos = 3)
+        public String customerName;
+    }
+
+    @CsvRecord(separator = ",", continueParseOnFailure = true)
+    public static class TolerantMultiBadOrder {
+        @DataField(pos = 1)
+        public int id;
+        @DataField(pos = 2, pattern = "yyyy-MM-dd", defaultValue = 
"1970-01-01")
+        public Date orderDate;
+        @DataField(pos = 3)
+        public String customerName;
+    }
+
+    @CsvRecord(separator = ",", continueParseOnFailure = true)
+    public static class TolerantEmptyDefaultOrder {
+        @DataField(pos = 1, defaultValue = "")
+        public int id;
+        @DataField(pos = 2, pattern = "yyyy-MM-dd")
+        public Date orderDate;
+        @DataField(pos = 3)
+        public String customerName;
+    }
+}
diff --git 
a/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fix/BindyKvpContinueOnParseFailureTest.java
 
b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fix/BindyKvpContinueOnParseFailureTest.java
new file mode 100644
index 000000000000..5bc79b21895e
--- /dev/null
+++ 
b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fix/BindyKvpContinueOnParseFailureTest.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dataformat.bindy.fix;
+
+import java.util.Date;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.dataformat.bindy.annotation.ContinueOnFailure;
+import org.apache.camel.dataformat.bindy.annotation.KeyValuePairField;
+import org.apache.camel.dataformat.bindy.annotation.Message;
+import org.apache.camel.dataformat.bindy.kvp.BindyKeyValuePairDataFormat;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * KeyValuePair parallel of BindyCsvContinueOnParseFailureTest. @Message 
controls the record-level
+ * default; @KeyValuePairField provides the field-level tri-state. Bad value 
on tag 2 (orderDate).
+ */
+public class BindyKvpContinueOnParseFailureTest extends CamelTestSupport {
+
+    private static final String BAD_ROW = "1=1 2=not-a-date 3=Alice";
+    private static final String GOOD_ROW = "1=1 2=2026-01-15 3=Alice";
+
+    @Test
+    public void recordUnset_fieldInherit_isStrict() {
+        assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:strict-default", BAD_ROW));
+    }
+
+    @Test
+    public void recordTolerant_fieldInherit_keepsRowWithNullField() throws 
Exception {
+        MockEndpoint mock = getMockEndpoint("mock:record-tolerant");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:record-tolerant", BAD_ROW);
+        MockEndpoint.assertIsSatisfied(context);
+
+        RecordTolerantOrder row = 
mock.getReceivedExchanges().get(0).getIn().getBody(RecordTolerantOrder.class);
+        assertNotNull(row);
+        assertEquals(1, row.id);
+        assertNull(row.orderDate);
+        assertEquals("Alice", row.customerName);
+    }
+
+    @Test
+    public void recordStrict_fieldInherit_isStrict() {
+        assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:record-strict", BAD_ROW));
+    }
+
+    @Test
+    public void recordTolerant_fieldFalse_fieldOverridesToStrict() {
+        assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:field-strict-override", 
BAD_ROW));
+    }
+
+    @Test
+    public void recordStrict_fieldTrue_fieldOverridesToTolerant() throws 
Exception {
+        MockEndpoint mock = getMockEndpoint("mock:field-tolerant-override");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:field-tolerant-override", BAD_ROW);
+        MockEndpoint.assertIsSatisfied(context);
+
+        FieldTolerantOverrideOrder row
+                = 
mock.getReceivedExchanges().get(0).getIn().getBody(FieldTolerantOverrideOrder.class);
+        assertNotNull(row);
+        assertEquals(1, row.id);
+        assertNull(row.orderDate);
+        assertEquals("Alice", row.customerName);
+    }
+
+    @Test
+    public void recordUnset_fieldTrue_keepsRowWithNullField() throws Exception 
{
+        MockEndpoint mock = getMockEndpoint("mock:field-tolerant");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:field-tolerant", BAD_ROW);
+        MockEndpoint.assertIsSatisfied(context);
+
+        FieldTolerantOrder row = 
mock.getReceivedExchanges().get(0).getIn().getBody(FieldTolerantOrder.class);
+        assertNotNull(row);
+        assertEquals(1, row.id);
+        assertNull(row.orderDate);
+        assertEquals("Alice", row.customerName);
+    }
+
+    @Test
+    public void tolerant_primitiveIntField_getsMinValue() throws Exception {
+        // @KeyValuePairField has no defaultValue element today, so the only 
fallback for a bad
+        // primitive int is getDefaultValueForPrimitive (which returns 
MIN_VALUE in Bindy).
+        MockEndpoint mock = getMockEndpoint("mock:tolerant-primitive-int");
+        mock.expectedMessageCount(1);
+        // tag 1 (int) is bad; tags 2 and 3 are valid
+        template.sendBody("direct:tolerant-primitive-int", "1=abc 2=2026-01-15 
3=Alice");
+        MockEndpoint.assertIsSatisfied(context);
+
+        TolerantPrimitiveIntOrder row
+                = 
mock.getReceivedExchanges().get(0).getIn().getBody(TolerantPrimitiveIntOrder.class);
+        assertNotNull(row);
+        assertEquals(Integer.MIN_VALUE, row.id);
+        assertNotNull(row.orderDate);
+        assertEquals("Alice", row.customerName);
+    }
+
+    @Test
+    public void recordTolerant_goodInput_parsesNormally() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:record-tolerant");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:record-tolerant", GOOD_ROW);
+        MockEndpoint.assertIsSatisfied(context);
+
+        RecordTolerantOrder row = 
mock.getReceivedExchanges().get(0).getIn().getBody(RecordTolerantOrder.class);
+        assertNotNull(row);
+        assertEquals(1, row.id);
+        assertNotNull(row.orderDate);
+        assertEquals("Alice", row.customerName);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:strict-default")
+                        .unmarshal(new 
BindyKeyValuePairDataFormat(StrictDefaultOrder.class))
+                        .to("mock:strict-default");
+                from("direct:record-tolerant")
+                        .unmarshal(new 
BindyKeyValuePairDataFormat(RecordTolerantOrder.class))
+                        .to("mock:record-tolerant");
+                from("direct:record-strict")
+                        .unmarshal(new 
BindyKeyValuePairDataFormat(RecordStrictOrder.class))
+                        .to("mock:record-strict");
+                from("direct:field-strict-override")
+                        .unmarshal(new 
BindyKeyValuePairDataFormat(FieldStrictOverrideOrder.class))
+                        .to("mock:field-strict-override");
+                from("direct:field-tolerant-override")
+                        .unmarshal(new 
BindyKeyValuePairDataFormat(FieldTolerantOverrideOrder.class))
+                        .to("mock:field-tolerant-override");
+                from("direct:field-tolerant")
+                        .unmarshal(new 
BindyKeyValuePairDataFormat(FieldTolerantOrder.class))
+                        .to("mock:field-tolerant");
+                from("direct:tolerant-primitive-int")
+                        .unmarshal(new 
BindyKeyValuePairDataFormat(TolerantPrimitiveIntOrder.class))
+                        .to("mock:tolerant-primitive-int");
+            }
+        };
+    }
+
+    @Message(pairSeparator = " ", keyValuePairSeparator = "=")
+    public static class StrictDefaultOrder {
+        @KeyValuePairField(tag = 1)
+        public int id;
+        @KeyValuePairField(tag = 2, pattern = "yyyy-MM-dd")
+        public Date orderDate;
+        @KeyValuePairField(tag = 3)
+        public String customerName;
+    }
+
+    @Message(pairSeparator = " ", keyValuePairSeparator = "=", 
continueParseOnFailure = true)
+    public static class RecordTolerantOrder {
+        @KeyValuePairField(tag = 1)
+        public int id;
+        @KeyValuePairField(tag = 2, pattern = "yyyy-MM-dd")
+        public Date orderDate;
+        @KeyValuePairField(tag = 3)
+        public String customerName;
+    }
+
+    @Message(pairSeparator = " ", keyValuePairSeparator = "=", 
continueParseOnFailure = false)
+    public static class RecordStrictOrder {
+        @KeyValuePairField(tag = 1)
+        public int id;
+        @KeyValuePairField(tag = 2, pattern = "yyyy-MM-dd")
+        public Date orderDate;
+        @KeyValuePairField(tag = 3)
+        public String customerName;
+    }
+
+    @Message(pairSeparator = " ", keyValuePairSeparator = "=", 
continueParseOnFailure = true)
+    public static class FieldStrictOverrideOrder {
+        @KeyValuePairField(tag = 1)
+        public int id;
+        @KeyValuePairField(tag = 2, pattern = "yyyy-MM-dd", 
continueParseOnFailure = ContinueOnFailure.FALSE)
+        public Date orderDate;
+        @KeyValuePairField(tag = 3)
+        public String customerName;
+    }
+
+    @Message(pairSeparator = " ", keyValuePairSeparator = "=", 
continueParseOnFailure = false)
+    public static class FieldTolerantOverrideOrder {
+        @KeyValuePairField(tag = 1)
+        public int id;
+        @KeyValuePairField(tag = 2, pattern = "yyyy-MM-dd", 
continueParseOnFailure = ContinueOnFailure.TRUE)
+        public Date orderDate;
+        @KeyValuePairField(tag = 3)
+        public String customerName;
+    }
+
+    @Message(pairSeparator = " ", keyValuePairSeparator = "=")
+    public static class FieldTolerantOrder {
+        @KeyValuePairField(tag = 1)
+        public int id;
+        @KeyValuePairField(tag = 2, pattern = "yyyy-MM-dd", 
continueParseOnFailure = ContinueOnFailure.TRUE)
+        public Date orderDate;
+        @KeyValuePairField(tag = 3)
+        public String customerName;
+    }
+
+    @Message(pairSeparator = " ", keyValuePairSeparator = "=", 
continueParseOnFailure = true)
+    public static class TolerantPrimitiveIntOrder {
+        @KeyValuePairField(tag = 1)
+        public int id;
+        @KeyValuePairField(tag = 2, pattern = "yyyy-MM-dd")
+        public Date orderDate;
+        @KeyValuePairField(tag = 3)
+        public String customerName;
+    }
+}
diff --git 
a/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/BindyFixedContinueOnParseFailureTest.java
 
b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/BindyFixedContinueOnParseFailureTest.java
new file mode 100644
index 000000000000..6c24f15f1337
--- /dev/null
+++ 
b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/BindyFixedContinueOnParseFailureTest.java
@@ -0,0 +1,303 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dataformat.bindy.fixed;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.dataformat.bindy.annotation.ContinueOnFailure;
+import org.apache.camel.dataformat.bindy.annotation.DataField;
+import org.apache.camel.dataformat.bindy.annotation.FixedLengthRecord;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Fixed-length parallel of BindyCsvContinueOnParseFailureTest. Each model has 
id (1 char), orderDate (10 chars,
+ * yyyy-MM-dd pattern), and customerName (10 chars, trimmed). The bad row 
supplies "notadate--" at the orderDate offset.
+ */
+public class BindyFixedContinueOnParseFailureTest extends CamelTestSupport {
+
+    private static final String BAD_ROW = "1notadate--Alice     ";
+    private static final String GOOD_ROW = "12026-01-15Alice     ";
+
+    @Test
+    public void recordUnset_fieldInherit_isStrict() {
+        assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:strict-default", BAD_ROW));
+    }
+
+    @Test
+    public void recordTolerant_fieldInherit_keepsRowWithNullField() throws 
Exception {
+        MockEndpoint mock = getMockEndpoint("mock:record-tolerant");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:record-tolerant", BAD_ROW);
+        MockEndpoint.assertIsSatisfied(context);
+
+        RecordTolerantOrder row = 
mock.getReceivedExchanges().get(0).getIn().getBody(RecordTolerantOrder.class);
+        assertNotNull(row);
+        assertEquals(1, row.id);
+        assertNull(row.orderDate);
+        assertEquals("Alice", row.customerName.trim());
+    }
+
+    @Test
+    public void recordStrict_fieldInherit_isStrict() {
+        assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:record-strict", BAD_ROW));
+    }
+
+    @Test
+    public void recordTolerant_fieldFalse_fieldOverridesToStrict() {
+        assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:field-strict-override", 
BAD_ROW));
+    }
+
+    @Test
+    public void recordStrict_fieldTrue_fieldOverridesToTolerant() throws 
Exception {
+        MockEndpoint mock = getMockEndpoint("mock:field-tolerant-override");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:field-tolerant-override", BAD_ROW);
+        MockEndpoint.assertIsSatisfied(context);
+
+        FieldTolerantOverrideOrder row
+                = 
mock.getReceivedExchanges().get(0).getIn().getBody(FieldTolerantOverrideOrder.class);
+        assertNotNull(row);
+        assertEquals(1, row.id);
+        assertNull(row.orderDate);
+        assertEquals("Alice", row.customerName.trim());
+    }
+
+    @Test
+    public void recordUnset_fieldTrue_keepsRowWithNullField() throws Exception 
{
+        MockEndpoint mock = getMockEndpoint("mock:field-tolerant");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:field-tolerant", BAD_ROW);
+        MockEndpoint.assertIsSatisfied(context);
+
+        FieldTolerantOrder row = 
mock.getReceivedExchanges().get(0).getIn().getBody(FieldTolerantOrder.class);
+        assertNotNull(row);
+        assertEquals(1, row.id);
+        assertNull(row.orderDate);
+        assertEquals("Alice", row.customerName.trim());
+    }
+
+    @Test
+    public void tolerant_withDefaultValue_substitutesParsedDefault() throws 
Exception {
+        MockEndpoint mock = getMockEndpoint("mock:tolerant-with-default");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:tolerant-with-default", BAD_ROW);
+        MockEndpoint.assertIsSatisfied(context);
+
+        TolerantWithDefaultValueOrder row
+                = 
mock.getReceivedExchanges().get(0).getIn().getBody(TolerantWithDefaultValueOrder.class);
+        assertNotNull(row);
+        assertEquals(1, row.id);
+        assertNotNull(row.orderDate);
+        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
+        assertEquals(fmt.parse("1970-01-01"), row.orderDate);
+        assertEquals("Alice", row.customerName.trim());
+    }
+
+    @Test
+    public void strict_withDefaultValue_stillThrows() {
+        assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:strict-with-default", 
BAD_ROW));
+    }
+
+    @Test
+    public void tolerant_malformedDefaultValue_throws() {
+        assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:tolerant-malformed-default", 
BAD_ROW));
+    }
+
+    @Test
+    public void tolerant_primitiveIntField_getsMinValue() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:tolerant-primitive-int");
+        mock.expectedMessageCount(1);
+        // id at pos 1 is the bad field; date and name are valid
+        template.sendBody("direct:tolerant-primitive-int", "x2026-01-15Alice   
  ");
+        MockEndpoint.assertIsSatisfied(context);
+
+        TolerantPrimitiveIntOrder row
+                = 
mock.getReceivedExchanges().get(0).getIn().getBody(TolerantPrimitiveIntOrder.class);
+        assertNotNull(row);
+        assertEquals(Integer.MIN_VALUE, row.id);    // Bindy's primitive 
default convention
+        assertNotNull(row.orderDate);
+        assertEquals("Alice", row.customerName.trim());
+    }
+
+    @Test
+    public void recordTolerant_goodInput_parsesNormally() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:record-tolerant");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:record-tolerant", GOOD_ROW);
+        MockEndpoint.assertIsSatisfied(context);
+
+        RecordTolerantOrder row = 
mock.getReceivedExchanges().get(0).getIn().getBody(RecordTolerantOrder.class);
+        assertNotNull(row);
+        assertEquals(1, row.id);
+        assertNotNull(row.orderDate);
+        assertEquals("Alice", row.customerName.trim());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:strict-default")
+                        .unmarshal(new 
BindyFixedLengthDataFormat(StrictDefaultOrder.class))
+                        .to("mock:strict-default");
+                from("direct:record-tolerant")
+                        .unmarshal(new 
BindyFixedLengthDataFormat(RecordTolerantOrder.class))
+                        .to("mock:record-tolerant");
+                from("direct:record-strict")
+                        .unmarshal(new 
BindyFixedLengthDataFormat(RecordStrictOrder.class))
+                        .to("mock:record-strict");
+                from("direct:field-strict-override")
+                        .unmarshal(new 
BindyFixedLengthDataFormat(FieldStrictOverrideOrder.class))
+                        .to("mock:field-strict-override");
+                from("direct:field-tolerant-override")
+                        .unmarshal(new 
BindyFixedLengthDataFormat(FieldTolerantOverrideOrder.class))
+                        .to("mock:field-tolerant-override");
+                from("direct:field-tolerant")
+                        .unmarshal(new 
BindyFixedLengthDataFormat(FieldTolerantOrder.class))
+                        .to("mock:field-tolerant");
+                from("direct:tolerant-with-default")
+                        .unmarshal(new 
BindyFixedLengthDataFormat(TolerantWithDefaultValueOrder.class))
+                        .to("mock:tolerant-with-default");
+                from("direct:strict-with-default")
+                        .unmarshal(new 
BindyFixedLengthDataFormat(StrictWithDefaultValueOrder.class))
+                        .to("mock:strict-with-default");
+                from("direct:tolerant-malformed-default")
+                        .unmarshal(new 
BindyFixedLengthDataFormat(TolerantMalformedDefaultOrder.class))
+                        .to("mock:tolerant-malformed-default");
+                from("direct:tolerant-primitive-int")
+                        .unmarshal(new 
BindyFixedLengthDataFormat(TolerantPrimitiveIntOrder.class))
+                        .to("mock:tolerant-primitive-int");
+            }
+        };
+    }
+
+    @FixedLengthRecord(length = 21)
+    public static class StrictDefaultOrder {
+        @DataField(pos = 1, length = 1)
+        public int id;
+        @DataField(pos = 2, length = 10, pattern = "yyyy-MM-dd")
+        public Date orderDate;
+        @DataField(pos = 3, length = 10, trim = true)
+        public String customerName;
+    }
+
+    @FixedLengthRecord(length = 21, continueParseOnFailure = true)
+    public static class RecordTolerantOrder {
+        @DataField(pos = 1, length = 1)
+        public int id;
+        @DataField(pos = 2, length = 10, pattern = "yyyy-MM-dd")
+        public Date orderDate;
+        @DataField(pos = 3, length = 10, trim = true)
+        public String customerName;
+    }
+
+    @FixedLengthRecord(length = 21, continueParseOnFailure = false)
+    public static class RecordStrictOrder {
+        @DataField(pos = 1, length = 1)
+        public int id;
+        @DataField(pos = 2, length = 10, pattern = "yyyy-MM-dd")
+        public Date orderDate;
+        @DataField(pos = 3, length = 10, trim = true)
+        public String customerName;
+    }
+
+    @FixedLengthRecord(length = 21, continueParseOnFailure = true)
+    public static class FieldStrictOverrideOrder {
+        @DataField(pos = 1, length = 1)
+        public int id;
+        @DataField(pos = 2, length = 10, pattern = "yyyy-MM-dd", 
continueParseOnFailure = ContinueOnFailure.FALSE)
+        public Date orderDate;
+        @DataField(pos = 3, length = 10, trim = true)
+        public String customerName;
+    }
+
+    @FixedLengthRecord(length = 21, continueParseOnFailure = false)
+    public static class FieldTolerantOverrideOrder {
+        @DataField(pos = 1, length = 1)
+        public int id;
+        @DataField(pos = 2, length = 10, pattern = "yyyy-MM-dd", 
continueParseOnFailure = ContinueOnFailure.TRUE)
+        public Date orderDate;
+        @DataField(pos = 3, length = 10, trim = true)
+        public String customerName;
+    }
+
+    @FixedLengthRecord(length = 21)
+    public static class FieldTolerantOrder {
+        @DataField(pos = 1, length = 1)
+        public int id;
+        @DataField(pos = 2, length = 10, pattern = "yyyy-MM-dd", 
continueParseOnFailure = ContinueOnFailure.TRUE)
+        public Date orderDate;
+        @DataField(pos = 3, length = 10, trim = true)
+        public String customerName;
+    }
+
+    @FixedLengthRecord(length = 21, continueParseOnFailure = true)
+    public static class TolerantWithDefaultValueOrder {
+        @DataField(pos = 1, length = 1)
+        public int id;
+        @DataField(pos = 2, length = 10, pattern = "yyyy-MM-dd", defaultValue 
= "1970-01-01")
+        public Date orderDate;
+        @DataField(pos = 3, length = 10, trim = true)
+        public String customerName;
+    }
+
+    @FixedLengthRecord(length = 21)
+    public static class StrictWithDefaultValueOrder {
+        @DataField(pos = 1, length = 1)
+        public int id;
+        @DataField(pos = 2, length = 10, pattern = "yyyy-MM-dd", defaultValue 
= "1970-01-01")
+        public Date orderDate;
+        @DataField(pos = 3, length = 10, trim = true)
+        public String customerName;
+    }
+
+    @FixedLengthRecord(length = 21, continueParseOnFailure = true)
+    public static class TolerantMalformedDefaultOrder {
+        @DataField(pos = 1, length = 1)
+        public int id;
+        @DataField(pos = 2, length = 10, pattern = "yyyy-MM-dd", defaultValue 
= "this-is-not-a-date")
+        public Date orderDate;
+        @DataField(pos = 3, length = 10, trim = true)
+        public String customerName;
+    }
+
+    @FixedLengthRecord(length = 21, continueParseOnFailure = true)
+    public static class TolerantPrimitiveIntOrder {
+        @DataField(pos = 1, length = 1)
+        public int id;
+        @DataField(pos = 2, length = 10, pattern = "yyyy-MM-dd")
+        public Date orderDate;
+        @DataField(pos = 3, length = 10, trim = true)
+        public String customerName;
+    }
+}
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
index 982c47367530..f75c5e87f73a 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
@@ -497,6 +497,42 @@ Hazelcast topic, queue, map, list, set, or in one of the 
repositories
 above must provide their own `Config` with a
 `JavaSerializationFilterConfig` configured for their class names.
 
+=== camel-bindy
+
+Bindy can now keep going when an individual field fails to parse, instead of 
aborting the whole
+unmarshal. Two new opt-in annotation elements drive this:
+
+* `continueParseOnFailure` on `@CsvRecord`, `@FixedLengthRecord`, and 
`@Message` (record-level boolean,
+  default `false`). When `true`, every field on the record tolerates parse 
failures by default.
+* `continueParseOnFailure` on `@DataField` and `@KeyValuePairField` 
(field-level tri-state of type
+  `ContinueOnFailure`, default `INHERIT`). `TRUE` and `FALSE` override the 
record-level setting
+  for that one field; `INHERIT` defers to the record.
+
+When a parse fails and the field is tolerant, Bindy substitutes the field's 
existing `@DataField.defaultValue`
+(if set) by running it through the same `Format` instance — so the substitute 
is automatically the
+right Java type. If no `defaultValue` is set, the field gets the same fallback 
an unfilled field would
+get today: `null` for object types, and the primitive's `MIN_VALUE` sentinel 
(the existing
+`getDefaultValueForPrimitive` convention) for `int`, `long`, etc. `boolean` 
falls back to `false` and
+`String` to `""` (or `null` if `defaultValueStringAsNull` is enabled on the 
DataFormat).
+
+Everything is opt-in; default behavior is unchanged. `@KeyValuePairField` does 
not have a `defaultValue`
+element today, so KVP fields can only fall back to the type-appropriate 
default.
+
+[source,java]
+----
+@CsvRecord(separator = ",", continueParseOnFailure = true)
+public class Order {
+    @DataField(pos = 1)
+    private int id;
+
+    @DataField(pos = 2, pattern = "yyyy-MM-dd", defaultValue = "1970-01-01")
+    private Date orderDate;     // bad input -> 1970-01-01 instead of throwing
+
+    @DataField(pos = 3, continueParseOnFailure = ContinueOnFailure.FALSE)
+    private BigDecimal amount;  // this one field stays strict
+}
+----
+
 === camel-keycloak
 
 The `KeycloakSecurityPolicy` route policy now always verifies the access token 
when one is present - signature,

Reply via email to