hasura/graphql-engine · error · TypecheckError

Expected a value of type {expected:} but got value {actual:}

Error message

Expected a value of type {expected:} but got value {actual:}

What it means

Part of the TypecheckError enum in the metadata-resolve crate. It is raised when a literal value supplied in metadata (e.g. a default argument value or comparison operand) fails to typecheck against the declared inbuilt scalar type (Int, Float, String, Boolean, ID). The error carries both the expected inbuilt type and the actual serde_json value so the mismatch is visible in the message.

Source

Thrown at v3/crates/metadata-resolve/src/helpers/typecheck.rs:14

//! Functions for typechecking JSON literals against expected types
use std::collections::{BTreeMap, BTreeSet};

use crate::stages::object_types;
use crate::types::error::ShouldBeAnError;
use crate::{Qualified, QualifiedBaseType, QualifiedTypeName, QualifiedTypeReference};
use open_dds::flags::Flag;
use open_dds::types::{CustomTypeName, FieldName};
use thiserror::Error;

#[derive(Error, Debug, PartialEq)]
/// Errors that can occur when typechecking a value
pub enum TypecheckError {
    #[error("Expected a value of type {expected:} but got value {actual:}")]
    ScalarTypeMismatch {
        expected: open_dds::types::InbuiltType,
        actual: serde_json::Value,
    },
    #[error("Error in array item: {inner_error:}")]
    ArrayItemMismatch { inner_error: Box<TypecheckError> },
    #[error("Expected an array but instead got value {value:}")]
    NonArrayValue { value: serde_json::Value },
    #[error("Expected a non-null value but received null")]
    NullInNonNullableColumn,
}

#[derive(Error, Debug, PartialEq)]
/// Issues that can occur when typechecking a value against an object type
pub enum TypecheckIssue {
    #[error("Expected an object value of type {expected:} but got value {actual:}")]
    ObjectTypeMismatch {
        expected: Qualified<CustomTypeName>,

View on GitHub (pinned to 724551b9ae)

Solutions

  1. Check the reported {expected:} inbuilt type and the {actual:} JSON value, then correct the literal in your metadata
  2. If the value should legitimately have that shape, change the declared argument/field type to match
  3. Quote/unquote the literal appropriately (e.g. remove quotes around a number expected to be Int)
  4. Re-run metadata resolution to confirm the typecheck passes

Example fix

// before
arguments:
  limit:
    type: Int
    default: "10"   # string, triggers ScalarTypeMismatch

// after
arguments:
  limit:
    type: Int
    default: 10
Defensive patterns

Strategy: validation

Validate before calling

// Rust: validate a literal against the inbuilt type before resolving metadata
fn matches_inbuilt(expected: &open_dds::types::InbuiltType, v: &serde_json::Value) -> bool {
    use open_dds::types::InbuiltType::*;
    match expected {
        Int | Float => v.is_number(),
        String | ID => v.is_string(),
        Boolean => v.is_boolean(),
    }
}

Try / catch

// When calling metadata-resolve stages, match on the error variant to surface field context:
match result {
    Err(resolve_error) => {
        if let Some(TypecheckError::ScalarTypeMismatch { expected, actual }) = extract_typecheck(&resolve_error) {
            eprintln!("literal {actual} does not match {expected:?}");
        }
    }
    Ok(_) => {}
}

Prevention

When it happens

Trigger: Calling the typecheck helper for a scalar-typed argument with a JSON value of the wrong kind, e.g. passing a string "42" where InbuiltType::Integer is expected, or a number where a Boolean is expected.

Common situations: Typos in metadata YAML/JSON defaults (quoting numbers), changing a scalar type in an object type without updating literal defaults, or generating metadata programmatically with serde_json values of the wrong JSON type.

Related errors


AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28). Data as JSON: /api/errors/d9ecd91e9adcaab6. Report an issue: GitHub.