nautechsystems/nautilus_trader · error

invalid signed E18 integer for {field}: {value}

Error message

invalid signed E18 integer for {field}: {value}

What it means

TWAP values arrive from RTDS as signed E18 fixed-point strings. decimal_from_signed_e18 validates the string is a (possibly minus-prefixed) pure ASCII-digit integer before parsing to i128 and scaling by 10^-18; any other character or empty string is rejected so a malformed mantissa never becomes a Decimal.

Source

Thrown at crates/adapters/polymarket/src/rtds.rs:1660

                wire: RtdsWireSubscription {
                    topic: RtdsTopic::EquityPrices.as_str(),
                    msg_type: "update",
                    filters: None,
                },
            }),
            other => anyhow::bail!("Unsupported RTDS custom data type: {other}"),
        }
    }
}

fn tracked_key(topic: &str, symbol_lower: &str) -> String {
    format!("{topic}:{symbol_lower}")
}

fn decimal_from_signed_e18(field: &str, value: &str) -> anyhow::Result<Decimal> {
    let digits = value.strip_prefix('-').unwrap_or(value);
    if digits.is_empty() || !digits.bytes().all(|byte| byte.is_ascii_digit()) {
        anyhow::bail!("invalid signed E18 integer for {field}: {value}");
    }

    let mantissa = value
        .parse::<i128>()
        .with_context(|| format!("signed E18 integer out of range for {field}: {value}"))?;
    Decimal::try_from_i128_with_scale(mantissa, 18)
        .with_context(|| format!("signed E18 value out of Decimal range for {field}: {value}"))
}

fn unix_nanos_from_millis(field: &str, value: u64) -> anyhow::Result<UnixNanos> {
    let millis = i64::try_from(value)
        .with_context(|| format!("millisecond timestamp out of range for {field}: {value}"))?;
    UnixNanos::from_millis_checked(millis)
        .with_context(|| format!("millisecond timestamp overflows UnixNanos for {field}: {value}"))
}

fn price_from_str(field: &str, value: &str) -> anyhow::Result<Price> {
    Price::from_decimal(parse_decimal_exact(value)?)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Log the raw field name and value; confirm which RTDS field is malformed
  2. Check the venue feed schema and update the parser if the field is no longer an integer string (e.g. accept decimal notation)
  3. Normalize the string before parsing (strip whitespace, reject/trim exponent notation) or parse as f64/Decimal directly when appropriate
  4. Report upstream feed corruption if the venue emits truncated frames

Example fix

// before
let digits = value.strip_prefix('-').unwrap_or(value);
if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_digit()) {
    anyhow::bail!("invalid signed E18 integer for {field}: {value}");
}
// after
let trimmed = value.trim();
let digits = trimmed.strip_prefix('-').unwrap_or(trimmed);
if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_digit()) {
    anyhow::bail!("invalid signed E18 integer for {field}: {value}");
}
Defensive patterns

Strategy: validation

Validate before calling

fn is_signed_e18(value: &str) -> bool {
    let d = value.strip_prefix('-').unwrap_or(value);
    !d.is_empty() && d.bytes().all(|b| b.is_ascii_digit())
}

Prevention

When it happens

Trigger: The venue sends a value field like "1.5", "1e6", "abc", "12_3", or "" for a signed E18 field; a truncated/partial JSON string value reaches the parser.

Common situations: Upstream schema change where a field becomes a JSON number or decimal-with-dot instead of an integer string; corrupted or cut-off frames; fields not expected to be E18 fed into the E18 parser by mistake.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/4f60df0bb1253870. Report an issue: GitHub.