fix(table): list the supported formats when a format table is rejected (#702)
diff --git a/crates/paimon/src/spec/core_options.rs b/crates/paimon/src/spec/core_options.rs
index f09a1e4..0e447a1 100644
--- a/crates/paimon/src/spec/core_options.rs
+++ b/crates/paimon/src/spec/core_options.rs
@@ -700,8 +700,8 @@
     /// default 32). Used as the per-operation fan-out limit for sorted BTree and
     /// bitmap shard reads, global-index vector search, and primary-key vector
     /// search. A value of `1` reproduces strict sequential execution. A
-    /// non-positive value is a misconfiguration and fails loud rather than being
-    /// silently clamped.
+    /// non-positive value, or one above [`MAX_GLOBAL_INDEX_THREAD_NUM`], is a
+    /// misconfiguration and fails loud rather than being silently clamped.
     pub fn global_index_thread_num(&self) -> crate::Result<usize> {
         let value = self
             .parse_i64_option(GLOBAL_INDEX_THREAD_NUM_OPTION)?
diff --git a/crates/paimon/src/table/format_table_scan.rs b/crates/paimon/src/table/format_table_scan.rs
index 4c83180..224ef50 100644
--- a/crates/paimon/src/table/format_table_scan.rs
+++ b/crates/paimon/src/table/format_table_scan.rs
@@ -615,6 +615,18 @@
         .ok()
 }
 
+fn supported_format_table_formats() -> Vec<&'static str> {
+    vec![
+        "parquet",
+        "orc",
+        "avro",
+        "row",
+        "mosaic",
+        #[cfg(feature = "vortex")]
+        "vortex",
+    ]
+}
+
 fn supported_format_table_extension(format: &str) -> crate::Result<&'static str> {
     match format.to_ascii_lowercase().as_str() {
         "parquet" => Ok(".parquet"),
@@ -626,7 +638,9 @@
         "vortex" => Ok(".vortex"),
         other => Err(crate::Error::Unsupported {
             message: format!(
-                "Format table file.format '{other}' is not supported by the Rust reader yet"
+                "Format table file.format '{other}' is not supported by the Rust reader yet, \
+                 expected one of: {}",
+                supported_format_table_formats().join(", ")
             ),
         }),
     }
@@ -656,3 +670,32 @@
         write_cols: None,
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_unsupported_format_lists_the_supported_ones() {
+        let error = supported_format_table_extension("csv").unwrap_err();
+        let crate::Error::Unsupported { message } = error else {
+            panic!("expected Unsupported, got {error:?}");
+        };
+        assert!(message.contains("'csv'"), "{message}");
+        for format in supported_format_table_formats() {
+            assert!(message.contains(format), "{format} missing from {message}");
+        }
+    }
+
+    #[test]
+    fn test_supported_formats_are_accepted_case_insensitively() {
+        for format in supported_format_table_formats() {
+            let expected = format!(".{format}");
+            assert_eq!(supported_format_table_extension(format).unwrap(), expected);
+            assert_eq!(
+                supported_format_table_extension(&format.to_ascii_uppercase()).unwrap(),
+                expected
+            );
+        }
+    }
+}
diff --git a/docs/src/sql.md b/docs/src/sql.md
index fc06266..34aa36a 100644
--- a/docs/src/sql.md
+++ b/docs/src/sql.md
@@ -2053,7 +2053,7 @@
 | `btree-index.fallback-scan-max-size` | `256mb` | Maximum total size of selected BTree global-index files for fallback scans used by range/between and suffix/contains/complex LIKE predicates; `0` disables BTree fallback index scans. |
 | `bitmap-index.fallback-scan-max-size` | `256mb` | Maximum total size of selected bitmap global-index files for fallback scans used by range/between and suffix/contains/complex LIKE predicates; `0` disables bitmap fallback index scans. |
 | `global-index.search-mode` | `fast` | Global index coverage mode for reads: `fast`, `full`, or `detail`. |
-| `global-index.thread-num` | `32` | Number of threads used to search global index fields concurrently; must be greater than 0. |
+| `global-index.thread-num` | `32` | Number of threads used to search global index fields concurrently; must be greater than 0 and must not exceed the runtime's task limit. |
 | `global-index.column-update-action` | `THROW_ERROR` | What a commit does when it updates an indexed column: `THROW_ERROR` rejects the commit, `DROP_PARTITION_INDEX` drops the affected partition index instead. |
 
 ### Variant Shredding Options