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

yiguolei pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.0 by this push:
     new 9812408dec8 branch-4.0: [improvement](error msg) prioritize retaining 
the error URL and first_error_msg when truncation occurs #56759 (#58145)
9812408dec8 is described below

commit 9812408dec811f098bff3828beb52ecdcefc7e4d
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Nov 20 09:55:29 2025 +0800

    branch-4.0: [improvement](error msg) prioritize retaining the error URL and 
first_error_msg when truncation occurs #56759 (#58145)
    
    Cherry-picked from #56759
    
    Co-authored-by: Refrain <[email protected]>
---
 .../insert/BaseExternalTableInsertExecutor.java    |  12 +-
 .../commands/insert/DictionaryInsertExecutor.java  |  13 +-
 .../trees/plans/commands/insert/InsertUtils.java   |  96 +++++++++
 .../insert/OlapGroupCommitInsertExecutor.java      |  13 +-
 .../plans/commands/insert/OlapInsertExecutor.java  |  12 +-
 .../plans/commands/insert/InsertUtilsTest.java     | 220 +++++++++++++++++++++
 .../fault_tolerance_nereids.groovy                 |   2 +-
 .../manual-partitioning.md.groovy                  |   5 +-
 .../insert_overwrite_auto_detect.groovy            |   6 +-
 .../test_iot_overwrite_and_create.groovy           |  10 +-
 .../insert_p0/test_error_msg_truncate.groovy       | 150 ++++++++++++++
 .../suites/insert_p0/test_insert_docs_demo.groovy  |   2 +-
 .../test_tvf_strict_mode_and_filter_ratio.groovy   |   2 +-
 .../test_auto_partition_behavior.groovy            |   2 +-
 .../test_list_partition_datatype.groovy            |   2 +-
 .../test_multi_column_partition.groovy             |   2 +-
 .../create/test_create_vault_with_kerberos.groovy  |   2 +-
 17 files changed, 514 insertions(+), 37 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/BaseExternalTableInsertExecutor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/BaseExternalTableInsertExecutor.java
index ccaf1229480..b1f2856b767 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/BaseExternalTableInsertExecutor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/BaseExternalTableInsertExecutor.java
@@ -135,18 +135,20 @@ public abstract class BaseExternalTableInsertExecutor 
extends AbstractInsertExec
         String queryId = DebugUtil.printId(ctx.queryId());
         // if any throwable being thrown during insert operation, first we 
should abort this txn
         LOG.warn("insert [{}] with query id {} failed", labelName, queryId, t);
-        StringBuilder sb = new StringBuilder(t.getMessage());
+        String firstErrorMsgPart = "";
+        String urlPart = "";
         if (txnId != INVALID_TXN_ID) {
             LOG.warn("insert [{}] with query id {} abort txn {} failed", 
labelName, queryId, txnId);
             if (!Strings.isNullOrEmpty(coordinator.getFirstErrorMsg())) {
-                sb.append(". first_error_msg: ").append(
-                        StringUtils.abbreviate(coordinator.getFirstErrorMsg(), 
Config.first_error_msg_max_length));
+                firstErrorMsgPart = 
StringUtils.abbreviate(coordinator.getFirstErrorMsg(),
+                        Config.first_error_msg_max_length);
             }
             if (!Strings.isNullOrEmpty(coordinator.getTrackingUrl())) {
-                sb.append(". url: ").append(coordinator.getTrackingUrl());
+                urlPart = coordinator.getTrackingUrl();
             }
         }
-        ctx.getState().setError(ErrorCode.ERR_UNKNOWN_ERROR, t.getMessage());
+        String finalErrorMsg = InsertUtils.getFinalErrorMsg(t.getMessage(), 
firstErrorMsgPart, urlPart);
+        ctx.getState().setError(ErrorCode.ERR_UNKNOWN_ERROR, finalErrorMsg);
 
         if (table instanceof ExternalTable) {
             try {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/DictionaryInsertExecutor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/DictionaryInsertExecutor.java
index 18afeb39cc8..d29a3350ba3 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/DictionaryInsertExecutor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/DictionaryInsertExecutor.java
@@ -28,7 +28,6 @@ import org.apache.doris.planner.PlanFragment;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.qe.StmtExecutor;
 
-import com.google.common.base.Strings;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -79,12 +78,14 @@ public class DictionaryInsertExecutor extends 
AbstractInsertExecutor {
         errMsg = t.getMessage() == null ? "unknown reason" : 
ddlException.getMessage();
         String queryId = DebugUtil.printId(ctx.queryId());
         LOG.warn("dictionary insert [{}] with query id {} failed", labelName, 
queryId, ddlException);
-        StringBuilder sb = new StringBuilder(errMsg);
-        if (!Strings.isNullOrEmpty(coordinator.getTrackingUrl())) {
-            sb.append(". url: ").append(coordinator.getTrackingUrl());
-        }
+
+        String finalErrorMsg = InsertUtils.getFinalErrorMsg(
+                errMsg,
+                "",
+                coordinator.getTrackingUrl()
+        );
         // we should set the context to make the caller know the command failed
-        ctx.getState().setError(ddlException.getMysqlErrorCode(), 
sb.toString());
+        ctx.getState().setError(ddlException.getMysqlErrorCode(), 
finalErrorMsg);
     }
 
     @Override
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
index 5e7d2c82f47..077e57daaa9 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
@@ -27,6 +27,7 @@ import org.apache.doris.catalog.Table;
 import org.apache.doris.catalog.TableIf;
 import org.apache.doris.common.Config;
 import org.apache.doris.common.FormatOptions;
+import org.apache.doris.common.util.DebugPointUtil;
 import org.apache.doris.datasource.hive.HMSExternalTable;
 import org.apache.doris.datasource.jdbc.JdbcExternalTable;
 import org.apache.doris.nereids.CascadesContext;
@@ -86,6 +87,7 @@ import org.apache.doris.transaction.TransactionEntry;
 import org.apache.doris.transaction.TransactionState;
 import org.apache.doris.transaction.TransactionStatus;
 
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
@@ -671,4 +673,98 @@ public class InsertUtils {
             }
         }
     }
+
+    /**
+     * Cut the error message to ensure it fits within MySQL error packet size 
limit.
+     * @param msg the main error message
+     * @param firstErrorMsg the first error message from coordinator
+     * @param url the tracking URL for error log
+     * @return the final truncated error message
+     */
+    public static String getFinalErrorMsg(String msg, String firstErrorMsg, 
String url) {
+        int maxTotalBytes = 512;
+
+        // For test
+        // 1. error msg length > 512, we will truncate it first to make sure 
the URL
+        //    and FirstErrorMsg keep complete.
+        // 2. if the FirstErrorMsg and URL length > 512 too, we will remind 
user use
+        //    `show load` too see detail
+        if (DebugPointUtil.isEnable("TestErrorMsgTruncate")) {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < 128; i++) {
+                sb.append("Test");
+            }
+            msg = sb.toString();
+            firstErrorMsg = sb.toString();
+            url = sb.toString();
+        }
+
+        // Calculate lengths first to avoid unnecessary string concatenation
+        int firstErrorMsgPartLen = 0;
+        int urlPartLen = 0;
+
+        if (!Strings.isNullOrEmpty(firstErrorMsg)) {
+            firstErrorMsgPartLen = ". first_error_msg: ".length() + 
firstErrorMsg.length();
+        }
+
+        if (!Strings.isNullOrEmpty(url)) {
+            urlPartLen = ". url: ".length() + url.length();
+        }
+
+        // use boolean too avoid string copy
+        boolean useFirstErrorMsgPlaceholder = false;
+        boolean useUrlPlaceholder = false;
+
+        // special case: url length > 512 or first error msg length > 512 or 
sum > 512
+        if (urlPartLen > maxTotalBytes && firstErrorMsgPartLen > 
maxTotalBytes) {
+            useUrlPlaceholder = true;
+            urlPartLen = ". url: please use `show load` for detail 
msg".length();
+            // only show once is enough
+            firstErrorMsgPartLen = 0;
+        } else if (urlPartLen > maxTotalBytes) {
+            useUrlPlaceholder = true;
+            urlPartLen = ". url: please use `show load` for detail 
msg".length();
+        } else if (firstErrorMsgPartLen > maxTotalBytes) {
+            useFirstErrorMsgPlaceholder = true;
+            firstErrorMsgPartLen = ". first_error_msg: please use `show load` 
for detail msg".length();
+        } else if (urlPartLen + firstErrorMsgPartLen > maxTotalBytes) {
+            int tempLen = ". url: please use `show load` for detail 
msg".length();
+            if (tempLen + firstErrorMsgPartLen > maxTotalBytes) {
+                // just leave firstErrorMsg
+                urlPartLen = 0;
+            } else {
+                useUrlPlaceholder = true;
+                urlPartLen = tempLen;
+            }
+        }
+
+        int maxMessageBytes = maxTotalBytes - firstErrorMsgPartLen - 
urlPartLen;
+
+        if (msg.length() > maxMessageBytes && maxMessageBytes > 0) {
+            msg = msg.substring(0, maxMessageBytes - 1);
+        }
+
+        StringBuilder finalErrorMsg = new StringBuilder();
+        finalErrorMsg.append(msg);
+
+        // Append firstErrorMsg part directly
+        if (firstErrorMsgPartLen > 0) {
+            if (useFirstErrorMsgPlaceholder) {
+                finalErrorMsg.append(". first_error_msg: please use `show 
load` for detail msg");
+            } else {
+                finalErrorMsg.append(". first_error_msg: 
").append(firstErrorMsg);
+            }
+        }
+
+        // Append url part directly
+        if (urlPartLen > 0) {
+            if (useUrlPlaceholder) {
+                finalErrorMsg.append(". url: please use `show load` for detail 
msg");
+            } else {
+                finalErrorMsg.append(". url: ").append(url);
+            }
+        }
+
+        return finalErrorMsg.toString();
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapGroupCommitInsertExecutor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapGroupCommitInsertExecutor.java
index 803b009d136..0b4d9026d91 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapGroupCommitInsertExecutor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapGroupCommitInsertExecutor.java
@@ -192,15 +192,18 @@ public class OlapGroupCommitInsertExecutor extends 
OlapInsertExecutor {
         String queryId = DebugUtil.printId(ctx.queryId());
         // if any throwable being thrown during insert operation, first we 
should abort this txn
         LOG.warn("insert [{}] with query id {} failed, url={}", labelName, 
queryId, coordinator.getTrackingUrl(), t);
-        StringBuilder sb = new StringBuilder(t.getMessage());
+        String firstErrorMsgPart = "";
+        String urlPart = "";
         if (!Strings.isNullOrEmpty(coordinator.getFirstErrorMsg())) {
-            sb.append(". first_error_msg: ").append(
-                    StringUtils.abbreviate(coordinator.getFirstErrorMsg(), 
Config.first_error_msg_max_length));
+            firstErrorMsgPart = 
StringUtils.abbreviate(coordinator.getFirstErrorMsg(),
+                    Config.first_error_msg_max_length);
         }
         if (!Strings.isNullOrEmpty(coordinator.getTrackingUrl())) {
-            sb.append(". url: ").append(coordinator.getTrackingUrl());
+            urlPart = coordinator.getTrackingUrl();
         }
-        ctx.getState().setError(ErrorCode.ERR_UNKNOWN_ERROR, sb.toString());
+
+        String finalErrorMsg = InsertUtils.getFinalErrorMsg(errMsg, 
firstErrorMsgPart, urlPart);
+        ctx.getState().setError(ErrorCode.ERR_UNKNOWN_ERROR, finalErrorMsg);
     }
 
     @Override
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapInsertExecutor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapInsertExecutor.java
index 7fceb289ef1..b078afd5abd 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapInsertExecutor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapInsertExecutor.java
@@ -290,15 +290,17 @@ public class OlapInsertExecutor extends 
AbstractInsertExecutor {
         if (Config.isCloudMode() && 
t.getMessage().contains(FeConstants.CLOUD_RETRY_E230)) {
             return;
         }
-        StringBuilder sb = new StringBuilder(t.getMessage());
+        String firstErrorMsgPart = "";
+        String urlPart = "";
         if (!Strings.isNullOrEmpty(coordinator.getFirstErrorMsg())) {
-            sb.append(". first_error_msg: ").append(
-                    StringUtils.abbreviate(coordinator.getFirstErrorMsg(), 
Config.first_error_msg_max_length));
+            firstErrorMsgPart = 
StringUtils.abbreviate(coordinator.getFirstErrorMsg(),
+                    Config.first_error_msg_max_length);
         }
         if (!Strings.isNullOrEmpty(coordinator.getTrackingUrl())) {
-            sb.append(". url: ").append(coordinator.getTrackingUrl());
+            urlPart = coordinator.getTrackingUrl();
         }
-        ctx.getState().setError(ErrorCode.ERR_UNKNOWN_ERROR, sb.toString());
+        String finalErrorMsg = InsertUtils.getFinalErrorMsg(errMsg, 
firstErrorMsgPart, urlPart);
+        ctx.getState().setError(ErrorCode.ERR_UNKNOWN_ERROR, finalErrorMsg);
     }
 
     @Override
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtilsTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtilsTest.java
new file mode 100644
index 00000000000..98bd8b1f5e9
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtilsTest.java
@@ -0,0 +1,220 @@
+// 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.doris.nereids.trees.plans.commands.insert;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test for InsertUtils.getFinalErrorMsg()
+ */
+public class InsertUtilsTest {
+
+    private static final int MAX_TOTAL_BYTES = 512;
+
+    private String generateString(int length) {
+        return generateString(length, "X");
+    }
+
+    private String generateString(int length, String prefix) {
+        StringBuilder sb = new StringBuilder(length);
+        sb.append(prefix);
+        for (int i = prefix.length(); i < length; i++) {
+            sb.append((char) ('A' + (i % 26)));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * case1: normal
+     */
+    @Test
+    public void testNormalCase() {
+        String msg = "Insert failed";
+        String firstErrorMsg = "Row format error";
+        String url = "http://example.com/error_log";;
+
+        String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+        Assertions.assertTrue(result.contains(msg));
+        Assertions.assertTrue(result.contains(firstErrorMsg));
+        Assertions.assertTrue(result.contains(url));
+        Assertions.assertTrue(result.contains("first_error_msg:"));
+        Assertions.assertTrue(result.contains("url:"));
+        Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+    }
+
+    /**
+     * case2: Msg is too long
+     */
+    @Test
+    public void testLongMsg() {
+        String msg = generateString(600);
+        String firstErrorMsg = "Short error";
+        String url = "http://example.com";;
+
+        String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+        Assertions.assertTrue(result.contains(firstErrorMsg));
+        Assertions.assertTrue(result.contains(url));
+        Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+        Assertions.assertTrue(result.indexOf(msg) == -1 || result.length() <= 
MAX_TOTAL_BYTES);
+    }
+
+    /**
+     * case3: firstErrorMsg is too long
+     */
+    @Test
+    public void testLongFirstErrorMsg() {
+        String msg = "Insert failed";
+        String firstErrorMsg = generateString(600);
+        String url = "http://example.com";;
+
+        String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+        Assertions.assertTrue(result.contains(msg));
+        Assertions.assertTrue(result.contains("please use `show load` for 
detail msg"));
+        Assertions.assertTrue(result.contains(url));
+        Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+        Assertions.assertFalse(result.contains(firstErrorMsg));
+    }
+
+    /**
+     * case4: url is too long
+     */
+    @Test
+    public void testLongUrl() {
+        String msg = "Insert failed";
+        String firstErrorMsg = "Row format error";
+        String url = "http://example.com/"; + generateString(600);
+
+        String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+        Assertions.assertTrue(result.contains(msg));
+        Assertions.assertTrue(result.contains(firstErrorMsg));
+        Assertions.assertTrue(result.contains("please use `show load` for 
detail msg"));
+        Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+        Assertions.assertFalse(result.contains(url));
+    }
+
+    /**
+     * case5:firstErrorMsg and url are too long
+     */
+    @Test
+    public void testBothFirstErrorMsgAndUrlTooLong() {
+        String msg = "Insert failed";
+        String firstErrorMsg = generateString(600);
+        String url = "http://example.com/"; + generateString(600);
+
+        String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+        Assertions.assertTrue(result.contains(msg));
+        Assertions.assertTrue(result.contains("please use `show load` for 
detail msg"));
+        Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+        Assertions.assertFalse(result.contains(firstErrorMsg));
+        Assertions.assertFalse(result.contains(url));
+    }
+
+    /**
+     * case6: firstErrorMsg , msg and url are too long
+     */
+    @Test
+    public void testAllParametersTooLong() {
+        String msg = generateString(600);
+        String firstErrorMsg = generateString(600);
+        String url = "http://example.com/"; + generateString(600);
+
+        String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+        Assertions.assertTrue(result.contains("please use `show load` for 
detail msg"));
+        Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+        Assertions.assertFalse(result.contains(msg));
+        Assertions.assertFalse(result.contains(firstErrorMsg));
+        Assertions.assertFalse(result.contains(url));
+    }
+
+    /**
+     * case7 :  msg length == 512
+     */
+    @Test
+    public void testMsgExactly512() {
+        String msg = generateString(512);
+        String firstErrorMsg = "";
+        String url = "";
+
+        String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+        Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+    }
+
+    /**
+     * case8: urlPartLen + firstErrorMsgPartLen > 512, but tempLen + 
firstErrorMsgPartLen still > 512
+     * Should only keep firstErrorMsg and drop url
+     */
+    @Test
+    public void testUrlAndFirstErrorMsgSumTooLong_DropUrl() {
+        String msg = "Insert failed";
+        // ". first_error_msg: ".length() = 19
+        // We need firstErrorMsgPartLen such that:
+        // ". url: please use `show load` for detail msg".length() + 
firstErrorMsgPartLen > 512
+        // ". url: please use `show load` for detail msg".length() = 47
+        // So firstErrorMsgPartLen should be > 465
+        // firstErrorMsgPartLen = 19 + firstErrorMsg.length() > 465
+        // firstErrorMsg.length() should be > 446
+        String firstErrorMsg = generateString(470, "ERROR_MSG_");
+        String url = generateString(100, "URL_");
+
+        String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+        Assertions.assertTrue(result.contains(msg));
+        Assertions.assertTrue(result.contains(firstErrorMsg));
+        Assertions.assertFalse(result.contains(url));
+        Assertions.assertFalse(result.contains("please use `show load` for 
detail msg"));
+        Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+    }
+
+    /**
+     * case9: urlPartLen + firstErrorMsgPartLen > 512, but tempLen + 
firstErrorMsgPartLen <= 512
+     * Should use url placeholder and keep firstErrorMsg
+     */
+    @Test
+    public void testUrlAndFirstErrorMsgSumTooLong_UseUrlPlaceholder() {
+        String msg = "Insert failed";
+        // ". first_error_msg: ".length() = 19
+        // ". url: ".length() = 7
+        // ". url: please use `show load` for detail msg".length() = 47
+        // We need: urlPartLen + firstErrorMsgPartLen > 512
+        // AND: 47 + firstErrorMsgPartLen <= 512
+        // So firstErrorMsgPartLen should be in range (512-urlPartLen, 465]
+        // Let's make firstErrorMsg.length() = 400, so firstErrorMsgPartLen = 
419
+        // And url.length() = 100, so urlPartLen = 107
+        // Then urlPartLen + firstErrorMsgPartLen = 526 > 512 ✓
+        // And 47 + 419 = 466 <= 512 ✓
+        String firstErrorMsg = generateString(400, "FIRST_ERROR_");
+        String url = generateString(100, "URL_CONTENT_");
+
+        String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+        Assertions.assertTrue(result.contains(msg));
+        Assertions.assertTrue(result.contains(firstErrorMsg));
+        Assertions.assertTrue(result.contains("please use `show load` for 
detail msg"));
+        Assertions.assertFalse(result.contains(url));
+        Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+    }
+}
+
diff --git 
a/regression-test/suites/ddl_p0/test_create_table_generated_column/fault_tolerance_nereids.groovy
 
b/regression-test/suites/ddl_p0/test_create_table_generated_column/fault_tolerance_nereids.groovy
index e85abae01ef..02409621bdc 100644
--- 
a/regression-test/suites/ddl_p0/test_create_table_generated_column/fault_tolerance_nereids.groovy
+++ 
b/regression-test/suites/ddl_p0/test_create_table_generated_column/fault_tolerance_nereids.groovy
@@ -175,7 +175,7 @@ suite("test_generated_column_fault_tolerance_nereids") {
     PROPERTIES("replication_num" = "1");
     ;"""
     // qt_common_default_test_insert_null
-    def exception_str = isGroupCommitMode() ? "too many filtered rows" : 
"Insert has filtered data in strict mode"
+    def exception_str = isGroupCommitMode() ? "too many filtered rows" : "null 
value for not null column"
     test {
         sql "INSERT INTO test_gen_col_common_ft(a,b) values(1,null);"
         exception exception_str
diff --git 
a/regression-test/suites/doc/table-design/data-partitioning/manual-partitioning.md.groovy
 
b/regression-test/suites/doc/table-design/data-partitioning/manual-partitioning.md.groovy
index 0ab6b1cc5f1..1e1e451240a 100644
--- 
a/regression-test/suites/doc/table-design/data-partitioning/manual-partitioning.md.groovy
+++ 
b/regression-test/suites/doc/table-design/data-partitioning/manual-partitioning.md.groovy
@@ -67,8 +67,9 @@ 
suite("docs/table-design/data-partitioning/manual-partitioning.md") {
             sql " insert into null_range2 values (null) "
             Assertions.fail("The SQL above should throw an exception as 
follows:\n\t\terrCode = 2, detailMessage = Insert has filtered data in strict 
mode. url: 
http://127.0.0.1:8040/api/_load_error_log?file=__shard_0/error_log_insert_stmt_b3a6d1f1fac74750-b3bb5d6e92a66da4_b3a6d1f1fac74750_b3bb5d6e92a66da4";)
         } catch (Exception e) {
-            assertTrue(e.getMessage().contains("errCode = 2, detailMessage = 
Insert has filtered data in strict mode."))
-            assertTrue(e.getMessage().contains("url:"))
+            log.info("Caught exception message: " + e.getMessage())
+            assertTrue(e.getMessage().contains("errCode = 2"))
+            assertTrue(e.getMessage().contains("no partition for this tuple"))
         }
     } catch (Throwable t) {
         Assertions.fail("examples in 
docs/table-design/data-partitioning/manual-partitioning.md failed to exec, 
please fix it", t)
diff --git 
a/regression-test/suites/insert_overwrite_p0/insert_overwrite_auto_detect.groovy
 
b/regression-test/suites/insert_overwrite_p0/insert_overwrite_auto_detect.groovy
index 6c3c7e7cdd7..ee7c7c64481 100644
--- 
a/regression-test/suites/insert_overwrite_p0/insert_overwrite_auto_detect.groovy
+++ 
b/regression-test/suites/insert_overwrite_p0/insert_overwrite_auto_detect.groovy
@@ -114,7 +114,8 @@ suite("test_iot_auto_detect") {
    } catch (Exception e) {
         log.info(e.getMessage())
         assertTrue(e.getMessage().contains('Insert has filtered data in strict 
mode') || 
-            e.getMessage().contains('Cannot found origin partitions in auto 
detect overwriting'))
+            e.getMessage().contains('Cannot found origin partitions in auto 
detect overwriting') ||
+            e.getMessage().contains('no partition for this tuple'))
     }
 
    sql " drop table if exists dt; "
@@ -138,7 +139,8 @@ suite("test_iot_auto_detect") {
       sql """ insert overwrite table dt partition(*) values ("2023-02-02"), 
("3000-12-12"); """
       check { result, exception, startTime, endTime ->
          assertTrue(exception.getMessage().contains('Insert has filtered data 
in strict mode') || 
-               exception.getMessage().contains('Cannot found origin partitions 
in auto detect overwriting'))
+               exception.getMessage().contains('Cannot found origin partitions 
in auto detect overwriting') ||
+               exception.getMessage().contains('no partition for this tuple'))
       }
    } 
    // test no rows(no partition hits) overwrite
diff --git 
a/regression-test/suites/insert_overwrite_p0/test_iot_overwrite_and_create.groovy
 
b/regression-test/suites/insert_overwrite_p0/test_iot_overwrite_and_create.groovy
index 4d0b667dd44..fa13377d881 100644
--- 
a/regression-test/suites/insert_overwrite_p0/test_iot_overwrite_and_create.groovy
+++ 
b/regression-test/suites/insert_overwrite_p0/test_iot_overwrite_and_create.groovy
@@ -44,11 +44,11 @@ suite("test_iot_overwrite_and_create") {
 
     test{
         sql """insert overwrite table auto_list partition(p1, p2) values 
("zzz");"""
-        exception "Insert has filtered data in strict mode."
+        exception "no partition for this tuple"
     }
     test{
         sql """insert overwrite table auto_list partition(p3) values 
("zzz3");"""
-        exception "Insert has filtered data in strict mode."
+        exception "no partition for this tuple"
     }
 
     sql """ insert into auto_list values 
("Beijing"),("Shanghai"),("xxx"),("list"),("1234567"); """
@@ -58,14 +58,14 @@ suite("test_iot_overwrite_and_create") {
     sql "set enable_auto_create_when_overwrite = false;"
     test{
         sql """insert overwrite table auto_list values ("zzz3");"""
-        exception "Insert has filtered data in strict mode."
+        exception "no partition for this tuple"
     }
     test{
         sql """insert overwrite table auto_list partition(p1, p2) values 
("zzz");"""
-        exception "Insert has filtered data in strict mode."
+        exception "no partition for this tuple"
     }
     test{
         sql """insert overwrite table auto_list partition(*) values 
("zzz3");"""
-        exception "Cannot found origin partitions in auto detect overwriting"
+        exception "no partition for this tuple"
     }
 }
diff --git a/regression-test/suites/insert_p0/test_error_msg_truncate.groovy 
b/regression-test/suites/insert_p0/test_error_msg_truncate.groovy
new file mode 100644
index 00000000000..df0e2e44497
--- /dev/null
+++ b/regression-test/suites/insert_p0/test_error_msg_truncate.groovy
@@ -0,0 +1,150 @@
+// 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.
+
+suite("test_error_msg_truncate","nonConcurrent") {
+    def tableName = "test_error_msg_truncate_table"
+    sql """ DROP TABLE IF EXISTS ${tableName} """
+    sql """
+        CREATE TABLE IF NOT EXISTS ${tableName} (
+            id INT,
+            value VARCHAR(10)
+        ) PROPERTIES (
+            "replication_num" = "1"
+        );
+        """
+    
+    GetDebugPoint().clearDebugPointsForAllFEs()
+    
+    try {
+        // case1: Test with debug point - error message should be truncated to 
512 bytes
+        GetDebugPoint().enableDebugPointForAllFEs("TestErrorMsgTruncate")
+        
+        def hasException = false
+        try {
+            sql """
+                INSERT INTO ${tableName} VALUES (1, 
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
+            """
+        } catch (Exception e) {
+            hasException = true
+            String errorMsg = e.getMessage()
+            logger.info("Case1 - Error message with debug point: " + errorMsg)
+            logger.info("Case1 - Error message length: " + errorMsg.length() + 
" bytes")
+            
+            // Check if error message contains "Test" (added by debug point)
+            assertTrue(errorMsg.contains("Test"), 
+                "Error message should contain 'Test' from debug point 
injection")
+            
+            // Check if error message contains url
+            assertTrue(errorMsg.contains("url:"), 
+                "Error message should contain 'url:'")
+
+            // Check if error message contains url
+            assertTrue(errorMsg.contains("please use `show load` for detail 
msg"), 
+                "Error message should contain 'please use `show load` for 
detail msg'")
+            
+            // Check if error message total length is within limit (512 bytes)
+            assertTrue(errorMsg.length() <= 512, 
+                "Error message length should not exceed 512 bytes, actual: " + 
errorMsg.length())
+            
+            logger.info("Case1 - Truncation test passed")
+        }
+        
+        assertTrue(hasException, "Should throw exception for inserting 
oversized value")
+        
+    } catch (Exception e) {
+        logger.info("Test failed with exception: " + e.getMessage())
+        throw e
+    } finally {
+        GetDebugPoint().clearDebugPointsForAllFEs()
+    }
+    
+    // case2: Test without debug point - verify first_error_msg and url content
+    try {
+        sql """
+            INSERT INTO ${tableName} VALUES (2, 
'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb');
+        """
+        fail("Should throw exception")
+    } catch (Exception e) {
+        String errorMsg = e.getMessage()
+        logger.info("Case2 - Normal error message: " + errorMsg)
+        logger.info("Case2 - Error message length: " + errorMsg.length() + " 
bytes")
+        
+        // Should NOT contain "Test" (no debug point)
+        assertFalse(errorMsg.contains("Test"), 
+            "Error message should NOT contain 'Test' without debug point")
+        
+        // Check if error message contains first_error_msg
+        assertTrue(errorMsg.contains("first_error_msg:"), 
+            "Error message should contain 'first_error_msg:'")
+        
+        // Extract and verify first_error_msg content
+        def firstErrorMsgPattern = ~/first_error_msg:\s*([^.]+(?:\.[^u])*)/
+        def matcher = errorMsg =~ firstErrorMsgPattern
+        if (matcher.find()) {
+            String firstErrorMsg = matcher.group(1).trim()
+            logger.info("Case2 - first_error_msg content: " + firstErrorMsg)
+            
+            // Verify it contains expected error information
+            assertTrue(firstErrorMsg.contains("column_name[value]"), 
+                "first_error_msg should mention the column name")
+            assertTrue(firstErrorMsg.contains("length"), 
+                "first_error_msg should mention length issue")
+            assertTrue(firstErrorMsg.contains("schema"), 
+                "first_error_msg should mention schema")
+            
+            logger.info("Case2 - first_error_msg validation passed")
+        } else {
+            fail("Could not extract first_error_msg from error message")
+        }
+        
+        // Check if error message contains url
+        assertTrue(errorMsg.contains("url:"), 
+            "Error message should contain 'url:'")
+        
+        // Extract and verify URL
+        def urlPattern = ~/url:\s*(http:\/\/[^\s]+)/
+        matcher = errorMsg =~ urlPattern
+        if (matcher.find()) {
+            String url = matcher.group(1).trim()
+            logger.info("Case2 - URL: " + url)
+            
+            // Verify URL format
+            assertTrue(url.startsWith("http://";), 
+                "URL should start with http://";)
+            assertTrue(url.contains("_load_error_log"), 
+                "URL should be a load error log URL")
+            assertTrue(url.contains("file="), 
+                "URL should contain file parameter")
+            
+            // Check URL contains necessary components
+            def urlComponents = url.split("\\?")
+            assertTrue(urlComponents.length == 2, 
+                "URL should have query parameters")
+            
+            def queryParams = urlComponents[1]
+            assertTrue(queryParams.contains("file="), 
+                "URL should have file parameter")
+            
+            logger.info("Case2 - URL format validation passed")
+            logger.info("Case2 - URL is complete and well-formed")
+        } else {
+            fail("Could not extract URL from error message")
+        }
+    }
+    
+    sql """ DROP TABLE IF EXISTS ${tableName} """
+}
diff --git a/regression-test/suites/insert_p0/test_insert_docs_demo.groovy 
b/regression-test/suites/insert_p0/test_insert_docs_demo.groovy
index 8e9ef72820b..53a900ef6bb 100644
--- a/regression-test/suites/insert_p0/test_insert_docs_demo.groovy
+++ b/regression-test/suites/insert_p0/test_insert_docs_demo.groovy
@@ -107,5 +107,5 @@ suite("test_insert_docs_demo") {
                 (NULL, "Alexander", 60),
                 (5, "Ava", 17);
         """
-    }, "Insert has too many filtered data 1/5 insert_max_filter_ratio is 0.1", 
"first_error_msg")
+    }, "null value for not null column", "first_error_msg")
 }
diff --git 
a/regression-test/suites/load_p0/tvf/test_tvf_strict_mode_and_filter_ratio.groovy
 
b/regression-test/suites/load_p0/tvf/test_tvf_strict_mode_and_filter_ratio.groovy
index 2506f210a81..3bb06993cb4 100644
--- 
a/regression-test/suites/load_p0/tvf/test_tvf_strict_mode_and_filter_ratio.groovy
+++ 
b/regression-test/suites/load_p0/tvf/test_tvf_strict_mode_and_filter_ratio.groovy
@@ -181,7 +181,7 @@ suite("test_tvf_strict_mode_and_filter_ratio", "p0") {
                                "column_separator" = "|"
                                );
                         """
-        exception """Insert has filtered data in strict mode"""
+        exception """null value for not null column"""
     }
     qt_sql_not_null_to_null_strict1 "select * from 
test_insert_select_tvf_strict_mode_and_filter_ratio order by 1"
 
diff --git 
a/regression-test/suites/partition_p0/auto_partition/test_auto_partition_behavior.groovy
 
b/regression-test/suites/partition_p0/auto_partition/test_auto_partition_behavior.groovy
index 5a862fcaa7d..9cbc291ce5f 100644
--- 
a/regression-test/suites/partition_p0/auto_partition/test_auto_partition_behavior.groovy
+++ 
b/regression-test/suites/partition_p0/auto_partition/test_auto_partition_behavior.groovy
@@ -166,7 +166,7 @@ suite("test_auto_partition_behavior") {
     sql """ insert into rewrite values ("Xxx"); """
     test {
         sql """ insert overwrite table rewrite partition(p1) values ("") """
-        exception "Insert has filtered data in strict mode"
+        exception "no partition for this tuple"
     }
     sql """ insert overwrite table rewrite partition(p1) values ("Xxx") """
     qt_sql_overwrite """ select * from rewrite """ // Xxx
diff --git 
a/regression-test/suites/partition_p0/list_partition/test_list_partition_datatype.groovy
 
b/regression-test/suites/partition_p0/list_partition/test_list_partition_datatype.groovy
index d678930d31a..2337004073e 100644
--- 
a/regression-test/suites/partition_p0/list_partition/test_list_partition_datatype.groovy
+++ 
b/regression-test/suites/partition_p0/list_partition/test_list_partition_datatype.groovy
@@ -352,7 +352,7 @@ suite("test_list_partition_datatype", "p0") {
         PROPERTIES ("replication_allocation" = "tag.location.default: 1")
         """
     sql """INSERT INTO test_list_partition_ddl_tbl_1 VALUES("0000-01-01", 
"0000-01-01"), ("9999-12-31", "9999-12-31")"""
-    def exception_str = isGroupCommitMode() ? "too many filtered rows" : 
"Insert has filtered data in strict mode"
+    def exception_str = isGroupCommitMode() ? "too many filtered rows" : "no 
partition for this tuple"
     test {
         sql """INSERT INTO test_list_partition_ddl_tbl_1 VALUES("2000-01-02", 
"2000-01-03")"""
         exception exception_str
diff --git 
a/regression-test/suites/partition_p0/multi_partition/test_multi_column_partition.groovy
 
b/regression-test/suites/partition_p0/multi_partition/test_multi_column_partition.groovy
index 3bb3c548c98..606f31f12fb 100644
--- 
a/regression-test/suites/partition_p0/multi_partition/test_multi_column_partition.groovy
+++ 
b/regression-test/suites/partition_p0/multi_partition/test_multi_column_partition.groovy
@@ -280,7 +280,7 @@ suite("test_multi_partition_key", "p0") {
             "values(0, NULL, 0, 0, 0, '2000-01-01 00:00:00', '2000-01-01', 
'a', 'a', 0.001, -0.001, 0.001)"
     qt_sql7 "select k1 from test_multi_col_test_partition_null_value 
partition(partition_a) where k2 is null"
     sql "ALTER TABLE test_multi_col_test_partition_null_value DROP PARTITION 
partition_a"
-    def exception_str = isGroupCommitMode() ? "too many filtered rows" : 
"Insert has filtered data in strict mode"
+    def exception_str = isGroupCommitMode() ? "too many filtered rows" : "no 
partition for this tuple"
     test {
         sql "insert into test_multi_col_test_partition_null_value " +
                 "values(0, NULL, 0, 0, 0, '2000-01-01 00:00:00', '2000-01-01', 
'a', 'a', 0.001, -0.001, 0.001)"
diff --git 
a/regression-test/suites/vault_p0/create/test_create_vault_with_kerberos.groovy 
b/regression-test/suites/vault_p0/create/test_create_vault_with_kerberos.groovy
index e774f3e3f31..dd946289df1 100644
--- 
a/regression-test/suites/vault_p0/create/test_create_vault_with_kerberos.groovy
+++ 
b/regression-test/suites/vault_p0/create/test_create_vault_with_kerberos.groovy
@@ -71,7 +71,7 @@ suite("test_create_vault_with_kerberos", "nonConcurrent") {
 
     expectExceptionLike({
         sql """ insert into ${tableName} values(1, 1); """
-    }, "Permission denied: user=not_exist_user")
+    }, "open file failed")
 
     expectExceptionLike({
         sql """


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to