juspay/hyperswitch · error · ApiErrorResponse

Invalid Calida metadata format

Error message

Invalid Calida metadata format

What it means

Thrown while building the X_CONNECTOR_CONFIG header for the Calida connector (ConnectorSpecificConfig::foreign_try_from via build_connector_config_header, connector_config.rs:1876). Calida requires HeaderKey auth, and when the merchant account carries a metadata JSON value, serde_json::from_value::<CalidaMetadata> must deserialize it into CalidaMetadata { shop_name: Secret<String> }. Any serde failure is collapsed into this single message, so the real cause (missing shop_name, non-string shop_name, or non-object metadata) is hidden.

Source

Thrown at crates/router/src/core/unified_connector_service/connector_config.rs:932

                ConnectorAuthType::MultiAuthKey {
                    api_key,
                    key1,
                    api_secret,
                    key2,
                } => Ok(Self::Fiservcommercehub {
                    api_key: api_key.clone(),
                    secret: api_secret.clone(),
                    merchant_id: key1.clone(),
                    terminal_id: key2.clone(),
                }),
                _ => Err(err("Fiservcommercehub requires MultiAuthKey auth type")),
            },
            Connector::Calida => match auth {
                ConnectorAuthType::HeaderKey { api_key } => {
                    let calida_meta = metadata
                        .map(|m| {
                            serde_json::from_value::<CalidaMetadata>(m.clone())
                                .map_err(|_| err("Invalid Calida metadata format"))
                        })
                        .transpose()?;

                    Ok(Self::Calida {
                        api_key: api_key.clone(),
                        shop_name: calida_meta.as_ref().map(|m| m.shop_name.clone()),
                    })
                }
                _ => Err(err("Calida requires HeaderKey auth type")),
            },
            Connector::Celero => match auth {
                ConnectorAuthType::HeaderKey { api_key } => Ok(Self::Celero {
                    api_key: api_key.clone(),
                }),
                _ => Err(err("Celero requires HeaderKey auth type")),
            },
            Connector::Helcim => match auth {
                ConnectorAuthType::HeaderKey { api_key } => Ok(Self::Helcim {

View on GitHub (pinned to 3093f22cc4)

Solutions

  1. Fix the merchant connector account metadata to a flat object with a string shop_name, e.g. {"shop_name": "my-shop"}, via the connector account update API.
  2. If the metadata was nested ({"calida": {...}}) remove the wrapper; CalidaMetadata is deserialized directly from the top-level value.
  3. If you do not need a shop name, set metadata to null/None entirely — metadata is Option and Calida builds fine without it (shop_name becomes None).
  4. As a library maintainer, replace .map_err(|_| err(...)) with .attach_printable/format! of the underlying serde error so the actual field problem is reported.

Example fix

// before — merchant connector account for Calida (metadata fails to deserialize)
"metadata": { "shop": "my-calida-shop" }   // wrong key name

// after
"metadata": { "shop_name": "my-calida-shop" }
Defensive patterns

Strategy: validation

Validate before calling

// Run before building the header / before relying on a Calida account
fn calida_metadata_ok(metadata: Option<&serde_json::Value>) -> bool {
    match metadata {
        None => true, // metadata is optional for Calida
        Some(v) => v
            .get("shop_name")
            .map(|s| s.is_string() && !s.as_str().unwrap_or_default().is_empty())
            .unwrap_or(false),
    }
}

Type guard

fn is_valid_calida_metadata(metadata: &serde_json::Value) -> bool {
    #[derive(serde::Deserialize)]
    struct CalidaMeta { shop_name: masking::Secret<String> }
    serde_json::from_value::<CalidaMeta>(metadata.clone()).is_ok()
}

Try / catch

// Rust: treat as a config error surfaced to the operator, not a runtime retry
let header = build_connector_config_header(connector, &auth, metadata.map(|m| m.borrow())).map_err(|e| {
    if e.current_context().message.contains("Calida metadata") {
        // actionable: point at the connector account's metadata field
        ReportConfigError { field: "metadata.shop_name", fix: "supply a string shop_name or remove metadata" }
    } else { e }
})?;

Prevention

When it happens

Trigger: A Calida merchant connector account was created with auth_type HeaderKey and a non-null metadata field, then any UCS (unified connector service) request path calls build_connector_config_header(Connector::Calida, auth, Some(metadata)). It fails when metadata is not a JSON object, lacks a shop_name key, or shop_name is not a string (e.g. {"shop_name": 12345} or {"shop": "x"}). Note the struct has no deny_unknown_fields, so extra keys are fine; only shop_name's presence and string type matter.

Common situations: Typo in the metadata field name (shop vs shop_name) when creating the merchant connector via the admin API; metadata reused from another connector (e.g. a nested {"calida": {"shop_name": ...}} wrapper); a dashboard/form that submits numbers for shop names; or an account created before shop_name metadata was required, later routed through the UCS path.

Related errors


AI-assisted analysis of juspay/hyperswitch@3093f22cc4 (2026-08-23). Data as JSON: /api/errors/53a747034837bf84. Report an issue: GitHub.