risingwavelabs/risingwave · error · CatalogError
{object_type} named {name} already exists{}
Error message
{object_type} named {name} already exists{} What it means
CatalogError::Duplicated is raised when creating a catalog object whose name already exists. It additionally reports whether the existing object is `under_creation` (still being created), which appends " and is still being created" to the message.
Source
Thrown at src/frontend/src/catalog/mod.rs:117
)).into())
} else {
Ok(())
}
}
pub type CatalogResult<T> = std::result::Result<T, CatalogError>;
// TODO(error-handling): provide more concrete error code for different object types.
#[derive(Error, Debug, thiserror_ext::Box)]
#[thiserror_ext(newtype(name = CatalogError, extra_provide = Self::provide_postgres_error_code))]
pub enum CatalogErrorInner {
#[error("{object_type} not found: {name}")]
NotFound {
object_type: &'static str,
name: String,
},
#[error(
"{object_type} named {name} already exists{}",
if *.under_creation { " and is still being created" } else { "" },
)]
Duplicated {
object_type: &'static str,
name: String,
under_creation: bool, // only used for StreamingJob type and Subscription for now
},
}
impl CatalogError {
/// Provide the Postgres error code for the error.
fn provide_postgres_error_code(&self, request: &mut std::error::Request<'_>) {
match self.inner() {
CatalogErrorInner::NotFound { object_type, .. } => {
// `database` not found should map to SQLSTATE 3D000 (Invalid Catalog Name),
// which is used by Postgres for non-existing database in startup.
if *object_type == "database" {View on GitHub (pinned to 6469eb736d)
Solutions
- Choose a different name or drop the existing object first (`DROP TABLE/MATERIALIZED VIEW <name>`).
- Use `CREATE ... IF NOT EXISTS` where supported to tolerate pre-existing objects.
- If the message says the object is still being created, wait for the ongoing creation to finish (or clean up the stuck object) before retrying.
Example fix
-- before CREATE MATERIALIZED VIEW mv AS SELECT ...; -- after DROP MATERIALIZED VIEW IF EXISTS mv; CREATE MATERIALIZED VIEW mv AS SELECT ...;
Defensive patterns
Strategy: try-catch
Validate before calling
const dup = await client.query(
"SELECT 1 FROM rw_tables WHERE name = $1", [name]);
if (dup.rows.length > 0) throw new Error(`name ${name} already in use`); Type guard
function nameIsFree(rows) { return Array.isArray(rows) && rows.length === 0; } Try / catch
try {
await client.query(`CREATE MATERIALIZED VIEW ${mv} AS ...`);
} catch (e) {
if (String(e.message).includes("already exists")) {
// drop and recreate, or reuse the existing object
} else throw e;
} Prevention
- Use IF NOT EXISTS for idempotent DDL scripts
- Serialize concurrent creations of the same name in deployment tooling
- On retries after failure, wait for or clean up objects stuck 'under creation'
When it happens
Trigger: `CREATE TABLE`/`CREATE MATERIALIZED VIEW`/`CREATE SOURCE` with a name that already exists in the schema; retrying a creation whose first attempt is still in-flight (the name is registered but creation is incomplete).
Common situations: Re-running idempotency-unaware migration scripts; concurrent jobs or deployments creating the same object name; retry after a failed creation that left the name registered as under-creation.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- {0} named {1} already exists{under_creation}
- {object_type} not found: {name}
- expect `CREATE TABLE` or `CREATE SOURCE` statement, found: `
- Unsupported throttle target: {:?} and throttle type: {:?}
- failed to drop column "{}" because it's referenced by a gene
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/39f3d44b17a069b9.
Report an issue: GitHub.