linera-io/linera-protocol · error

expected an operation name after 'query', e.g. 'query MyQuer

Error message

expected an operation name after 'query', e.g. 'query MyQuery {{ ... }}'

What it means

After consuming 'query' plus whitespace, parse_allowed_subscription extracts the leading alphanumeric/underscore run as the operation name; the name is the key clients later use to reference this subscription. This error fires when that run is empty — i.e. the text immediately after 'query ' is punctuation such as '{', ':' or end-of-string, so no name exists to register the query under.

Source

Thrown at linera-service/src/query_subscription.rs:45

/// Parses a GraphQL query string like `query Name { ... }` and extracts the operation name.
pub fn parse_allowed_subscription(s: &str) -> anyhow::Result<RegisteredQuery> {
    let trimmed = s.trim();
    let rest = trimmed
        .strip_prefix("query")
        .ok_or_else(|| anyhow::anyhow!("expected query to start with 'query', got: {s}"))?;
    // The character right after "query" must be whitespace (not part of a longer word).
    anyhow::ensure!(
        rest.starts_with(char::is_whitespace),
        "expected whitespace after 'query' keyword"
    );
    let rest = rest.trim_start();
    // Extract the operation name: sequence of alphanumeric/underscore chars.
    let name = rest
        .split(|c: char| !c.is_alphanumeric() && c != '_')
        .next()
        .unwrap_or_default();
    anyhow::ensure!(
        !name.is_empty(),
        "expected an operation name after 'query', e.g. 'query MyQuery {{ ... }}'"
    );
    Ok(RegisteredQuery {
        name: name.to_string(),
        query: trimmed.to_string(),
    })
}

/// Parses a `Name=Secs` string into a query name and TTL in seconds.
pub fn parse_subscription_ttl(s: &str) -> Result<(String, u64), String> {
    let (name, secs) = s
        .split_once('=')
        .ok_or_else(|| format!("expected format Name=Secs, got: {s}"))?;
    let secs: u64 = secs
        .parse()
        .map_err(|e| format!("invalid seconds value '{secs}': {e}"))?;
    Ok((name.to_string(), secs))

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Name the operation: 'query { transfers { id } }' becomes 'query Transfers { transfers { id } }'
  2. Make sure the whole query string is one shell argument (quote it) so the name and body are not lost
  3. Use the same name later in --subscription-ttl entries and in subscription requests, since the extracted name is the registry key

Example fix

# before
--allow-subscription 'query { transfers { id } }'
# after
--allow-subscription 'query Transfers { transfers { id } }'
Defensive patterns

Strategy: validation

Validate before calling

// Rust: require a non-empty name after 'query'
fn subscription_has_name(s: &str) -> bool {
    let t = s.trim();
    let Some(rest) = t.strip_prefix("query") else { return false };
    if !rest.starts_with(char::is_whitespace) { return false; }
    let rest = rest.trim_start();
    let name = rest.split(|c: char| !c.is_alphanumeric() && c != '_').next().unwrap_or_default();
    !name.is_empty()
}

Type guard

fn has_named_query_operation(s: &str) -> bool { subscription_has_name(s) }

Try / catch

if let Err(e) = parse_allowed_subscription(&arg) {
    log::warn!("skipping subscription {arg:?}: {e:#}");
    failures.push(arg);
}

Prevention

When it happens

Trigger: Passing an anonymous GraphQL operation via --allow-subscription, e.g. 'query { transfers { id } }', or something degenerate like 'query :' or 'query '. Parsing happens at node startup inside run(), so the process exits immediately.

Common situations: Anonymous queries are legal GraphQL and most clients accept them, so users paste a working query from graphiql/curl only to have the node reject it; also truncated arguments cut off by shell word-splitting.

Related errors


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