risingwavelabs/risingwave · error · anyhow::Error
Invalid iceberg table identifier
Error message
Invalid iceberg table identifier '{}': identifier parts must not be empty What it means
The Iceberg table identifier built from `database_name` and `table_name` with-properties produced an empty part after splitting on '.'. The connector refuses identifiers whose namespace or table segment is empty because Iceberg catalogs require non-empty identifier components. `full_identifier()` echoes the offending value so the developer can see what was parsed.
Solutions
- Set a non-empty `table.name` property with no leading/trailing/embedded dots.
- If `database.name` is supplied, ensure it is non-empty and each dot-separated segment is non-empty.
- Check upstream variable substitution or SQL generation that may inject an empty value.
Example fix
// before create table t with ( connector = 'iceberg', database.name = 'db.', table.name = 'events' ); // after create table t with ( connector = 'iceberg', database.name = 'db', table.name = 'events' );
Defensive patterns
Strategy: validation
Validate before calling
function validateIcebergIdentifier(db, table) {
const parts = [...(db ?? '').split('.'), ...table.split('.')];
if (parts.some(p => p.length === 0)) {
throw new Error(`Invalid iceberg identifier: ${db ? db + '.' : ''}${table}`);
}
} Type guard
const isValidIdentifier = (s) => typeof s === 'string' && s.split('.').every(p => p.length > 0); Prevention
- Trim identifiers and reject empty strings at config load time.
- Never build identifiers by naive string concatenation with dots from user input.
- Add a CI/unit test covering dotted-identifier parsing edge cases.
When it happens
Trigger: Calling `identifier_parts` (via `to_table_ident` or `validate`) with `table_name` set to "", starting/ending with '.', containing consecutive dots, or a `database_name` that is empty or has empty segments (e.g. "a..b", ".tbl", "db.").
Common situations: Users writing `create table ... with (table.name = '')` or `database.name = 'db.'`; template/variable interpolation leaving a placeholder empty; copy-paste of dotted identifiers from another system with trailing separators.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- `commit-checkpoint-interval` must be greater than 0
- `compaction.delete_files_count_threshold` must be greater…
- `compaction_interval_sec` must be greater than 0
- `compaction.max_snapshots_num` must be greater than 0
- `compaction.small_files_threshold_mb` must be greater than 0
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/56b80a309c7c01bb.
Report an issue: GitHub.
Appendix: source
Thrown at src/connector/src/connector_common/iceberg/mod.rs:270
impl IcebergTableIdentifier {
pub fn database_name(&self) -> Option<&str> {
self.database_name.as_deref()
}
pub fn table_name(&self) -> &str {
&self.table_name
}
fn identifier_parts(&self) -> ConnectorResult<Vec<&str>> {
let mut parts = Vec::new();
if let Some(database_name) = &self.database_name {
parts.extend(database_name.split('.'));
}
parts.extend(self.table_name.split('.'));
if parts.iter().any(|part| part.is_empty()) {
bail!(
"Invalid iceberg table identifier '{}': identifier parts must not be empty",
self.full_identifier()
);
}
Ok(parts)
}
fn full_identifier(&self) -> String {
match &self.database_name {
Some(database_name) => format!("{}.{}", database_name, self.table_name),
None => self.table_name.clone(),
}
}
pub fn to_table_ident(&self) -> ConnectorResult<TableIdent> {
let ret = TableIdent::from_strs(self.identifier_parts()?);
View on GitHub (pinned to 6469eb736d)