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
- Log the raw field name and value; confirm which RTDS field is malformed
- Check the venue feed schema and update the parser if the field is no longer an integer string (e.g. accept decimal notation)
- Normalize the string before parsing (strip whitespace, reject/trim exponent notation) or parse as f64/Decimal directly when appropriate
- 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
- Validate E18 fields with is_signed_e18 before conversion
- Keep parser in sync with venue feed schema changes
- Log raw payloads on parse failure for diagnosis
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
- Failed to parse tick size '{new_tick_size}': {e}
- Empty book snapshot for {instrument_id}
- invalid {field}='{raw}': {e}
- invalid price `{value}`: {e}
- invalid quantity `{value}`: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/4f60df0bb1253870.
Report an issue: GitHub.