linera-io/linera-protocol · error

unexpected query response: {other:?}

Error message

unexpected query response: {other:?}

What it means

The bridge monitor sent a user application query (isDepositProcessed) to a Linera chain via chain_client.query_application, but the returned QueryOutcome was not QueryResponse::User(bytes). Only the User variant carries a GraphQL service response; receiving another variant means the query was answered (or rejected) at the system level instead of being routed to the application's service.

Source

Thrown at linera-bridge/src/monitor/mod.rs:49

use linera_core::{client::ChainClient, environment::Environment};
use linera_execution::{Query, QueryResponse};
use tokio::sync::RwLock;

use crate::proof::DepositKey;

/// Queries the evm-bridge app to check whether a deposit has been processed on Linera.
pub async fn query_deposit_processed<E: Environment>(
    chain_client: &ChainClient<E>,
    bridge_app_id: ApplicationId,
    deposit_key: &DepositKey,
) -> anyhow::Result<bool> {
    let hash_hex = format!("0x{}", hex::encode(deposit_key.hash()));
    let gql = format!(r#"{{ isDepositProcessed(hash: "{hash_hex}") }}"#);
    let query = Query::user_without_abi(bridge_app_id, &GqlRequest { query: gql })?;
    let (outcome, _) = chain_client.query_application(query, None).await?;
    let response_bytes = match outcome.response {
        QueryResponse::User(bytes) => bytes,
        other => anyhow::bail!("unexpected query response: {other:?}"),
    };
    let response: serde_json::Value = serde_json::from_slice(&response_bytes)?;
    Ok(response["data"]["isDepositProcessed"].as_bool() == Some(true))
}

/// Queries the wrapped-fungible app for its declared source-ERC-20 decimals.
pub async fn query_wrapped_fungible_decimals<E: Environment>(
    chain_client: &ChainClient<E>,
    fungible_app_id: ApplicationId,
) -> anyhow::Result<u8> {
    let query = Query::user_without_abi(
        fungible_app_id,
        &GqlRequest {
            query: "{ decimals }".to_string(),
        },
    )?;
    let (outcome, _) = chain_client.query_application(query, None).await?;
    let response_bytes = match outcome.response {

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Verify bridge_app_id: it must be the application id of the published bridge app (match it against the id printed at publish time / the chain's registered applications).
  2. Confirm the target chain actually has the bridge application registered (query the chain's application list) before calling query_deposit_processed.
  3. Redeploy or re-register the application if it is missing, then update the relay configuration.
  4. Handle the error at the call site: treat any non-User response as a configuration fault and stop rather than retry, since retrying with the same ids cannot succeed.

Example fix

// before
let (outcome, _) = chain_client.query_application(query, None).await?;
let QueryResponse::User(bytes) = outcome.response else {
    anyhow::bail!("unexpected query response: {:?}", outcome.response);
};

// after: fail with actionable context
let QueryResponse::User(bytes) = outcome.response else {
    anyhow::bail!(
        "bridge app {bridge_app_id} did not return a user query response \
         (got {:?}); is the application registered on chain {}?",
        outcome.response, chain_client.chain_id()
    );
};
Defensive patterns

Strategy: try-catch

Validate before calling

// Before starting the monitor, verify the bridge app is registered on the target chain
// (e.g. query the chain's applications and assert bridge_app_id is present),
// so query_deposit_processed cannot hit a system-level response.

Try / catch

match monitor.query_deposit_processed(&key).await {
    Ok(processed) => processed,
    Err(e) if e.to_string().contains("unexpected query response") => {
        // configuration fault: wrong/unregistered app id — do NOT retry blindly
        tracing::error!(%bridge_app_id, "bridge app not answering user queries; check registration");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling query_deposit_processed with a bridge_app_id that is not a user application (wrong ApplicationId, or the app was never published/registered on the target chain), or a version/ABI mismatch where the node resolves the query to a system response. The response enum is matched strictly; any non-User variant bails with its Debug form.

Common situations: Misconfigured BRIDGE_APP_ID env/config pointing at a fungible-token app id instead of the bridge app; querying a chain where the bridge application was never registered; deployment drift between the relay config and what is actually published on-chain.

Related errors


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