linera-io/linera-protocol · error

Expected an `ExecutionError`. Got: {self:#?}

Error message

Expected an `ExecutionError`. Got: {self:#?}

What it means

expect_execution_error (only built with with_testing) unwraps a WorkerError into its inner ExecutionError, asserting the error is a WorkerError::ChainError first. This panic means the worker returned a different top-level variant. Note a subtlety: ExecutionError::BlobsNotFound and EventsNotFound are hoisted to top-level WorkerError variants by the From impl, so an execution error of those kinds ALSO lands here.

Source

Thrown at linera-core/src/worker.rs:534

                    execution_error,
                    context,
                ))),
            },
            error => Self::ChainError(Box::new(error)),
        }
    }
}

#[cfg(with_testing)]
impl WorkerError {
    /// Returns the inner [`ExecutionError`] in this error.
    ///
    /// # Panics
    ///
    /// If this is not caused by an [`ExecutionError`].
    pub fn expect_execution_error(self, expected_context: ChainExecutionContext) -> ExecutionError {
        let WorkerError::ChainError(chain_error) = self else {
            panic!("Expected an `ExecutionError`. Got: {self:#?}");
        };

        let ChainError::ExecutionError(execution_error, context) = *chain_error else {
            panic!("Expected an `ExecutionError`. Got: {chain_error:#?}");
        };

        assert_eq!(context, expected_context);

        *execution_error
    }
}

type ChainWorkerArc<S> = Arc<tokio::sync::RwLock<ChainWorkerState<S>>>;
type ChainWorkerWeak<S> = std::sync::Weak<tokio::sync::RwLock<ChainWorkerState<S>>>;
type ChainWorkerFuture<S> = Shared<oneshot::Receiver<ChainWorkerWeak<S>>>;

/// Each map entry is a `Shared<oneshot::Receiver<Weak<...>>>`:
///

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Look at the {self:#?} dump in the panic — it shows the actual variant; handle that variant (e.g. match WorkerError::BlobsNotFound) or fix setup so the expected execution error occurs.
  2. If blobs/events are genuinely part of the scenario, publish them in the fixture first.
  3. Check the From<ChainError> hoisting of BlobsNotFound/EventsNotFound when reasoning about which variant you will actually receive.

Example fix

// before
let err = worker.handle_certificate(...).unwrap_err();
let exec = err.expect_execution_error(ctx); // panics: got BlobsNotFound

// after
match worker.handle_certificate(...).unwrap_err() {
    WorkerError::BlobsNotFound(ids) => { /* publish blobs in fixture */ }
    other => { let _ = other.expect_execution_error(ctx); }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Rust — assert the shape before unwrapping
if !matches!(err, WorkerError::ChainError(_)) {
    // handle BlobsNotFound / EventsNotFound / other variants explicitly
}

Type guard

fn is_execution_error(err: &WorkerError) -> bool {
    matches!(err, WorkerError::ChainError(inner) if matches!(**inner, ChainError::ExecutionError(_, _)))
    // note: BlobsNotFound/EventsNotFound are hoisted to top-level WorkerError variants
}

Try / catch

match err {
    WorkerError::BlobsNotFound(ids) => { /* publish blobs in the fixture */ }
    WorkerError::EventsNotFound(ids) => { /* publish events in the fixture */ }
    other => { let _exec = other.expect_execution_error(ctx); }
}

Prevention

When it happens

Trigger: In tests: the code under test was expected to fail with an execution error but instead produced WorkerError::BlobsNotFound / WorkerError::EventsNotFound (missing blobs/events surfaced at upload), an arity/protocol error, or succeeded and returned a different wrapper.

Common situations: Test fixtures forgetting to publish required blobs before running the block, so the worker reports BlobsNotFound instead of the anticipated user-code error; test flakiness where an earlier setup step failed; refactors changing which layer emits the error.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/fb3f43550822acfa. Report an issue: GitHub.