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

  1. Choose a different name or drop the existing object first (`DROP TABLE/MATERIALIZED VIEW <name>`).
  2. Use `CREATE ... IF NOT EXISTS` where supported to tolerate pre-existing objects.
  3. 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

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


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/39f3d44b17a069b9. Report an issue: GitHub.