risingwavelabs/risingwave · error · MetaError

no frontend worker available

Error message

no frontend worker available

What it means

In src/meta/service/src/ddl_service.rs:1354, the service lists running frontend worker nodes and randomly picks one to proxy the request; if the list is empty, `choose` returns None and this MetaError is produced. It is the steady-state guard for any DDL path that requires a live frontend.

Source

Thrown at src/meta/service/src/ddl_service.rs:1354

        Ok(Response::new(AlterStreamingJobConfigResponse {}))
    }

    /// Auto schema change for cdc sources,
    /// called by the source parser when a schema change is detected.
    async fn auto_schema_change(
        &self,
        request: Request<AutoSchemaChangeRequest>,
    ) -> Result<Response<AutoSchemaChangeResponse>, Status> {
        let req = request.into_inner();

        // randomly select a frontend worker to get the replace table plan
        let workers = self
            .metadata_manager
            .list_worker_node(Some(WorkerType::Frontend), Some(State::Running))
            .await?;
        let worker = workers
            .choose(&mut thread_rng())
            .ok_or_else(|| MetaError::from(anyhow!("no frontend worker available")))?;

        let client = self
            .env
            .frontend_client_pool()
            .get(worker)
            .await
            .map_err(MetaError::from)?;

        let Some(schema_change) = req.schema_change else {
            return Err(Status::invalid_argument(
                "schema change message is required",
            ));
        };

        for table_change in schema_change.table_changes {
            for c in &table_change.columns {
                let c = ColumnCatalog::from(c.clone());

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Start or restart frontend node(s) and confirm they reach Running state before retrying.
  2. Check frontend meta-heartbeat connectivity/ports if frontends register but stay non-running.
  3. Retry the request after the cluster scales back up; the pick is random per request.
  4. If running meta-only tests, register a mock/real frontend worker before calling this DDL path.

Example fix

// before: call with no running frontend
// after
$ ./risedev d  # start cluster incl. frontend
$ ./risedev psql -c "SHOW WORKERS;"  # verify frontend RUNNING
$ ./risedev psql -c "<your ddl statement;>"
Defensive patterns

Strategy: retry

Validate before calling

const running = (await metaClient.listWorkers()).filter(w => w.type === 'Frontend' && w.state === 'Running');
if (running.length === 0) throw new Error('Precheck: no frontend worker available');

Type guard

const hasFrontend = ws => Array.isArray(ws) && ws.some(w => w.workerType === 'FRONTEND' && w.state === 'RUNNING');

Try / catch

try {
  await ddlClient.proxyOperation(req);
} catch (e) {
  if (e.message?.includes('no frontend worker available')) {
    await backoffRetry(() => ddlClient.proxyOperation(req), { retries: 3, on: waitForFrontend });
  } else { throw e; }
}

Prevention

When it happens

Trigger: Invoking the DDL service operation at ddl_service.rs:1354 (frontend-proxied request) when no Frontend worker is in Running state — e.g. no frontend joined yet or all frontends are down.

Common situations: Meta-only operations issued directly against the meta node without a frontend in the cluster, or all frontends crashed mid-session.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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