xai-org/x-algorithm · error

QueryFields should not be decoded from Thrift

Error message

QueryFields should not be decoded from Thrift

What it means

SafetyLevel is encoded as a Thrift i32 (write side only); from_thrift panics because the type was never meant to be read back from Thrift. The panic message mentions QueryFields, hinting this decode path was copied from a sibling encode-only type and is intentionally unreachable.

Source

Thrown at visibility-filtering-client/vf_client.rs:98

        self.safety_level.to_thrift(proto);
        proto.write_field_end().unwrap();
        proto
            .write_field_begin(&TFieldIdentifier::new("for_user_id", TType::I64, 2))
            .unwrap();
        proto.write_i64(self.for_user_id as i64).unwrap();
        proto.write_field_end().unwrap();
        proto.write_field_stop().unwrap();
        proto.write_struct_end().unwrap();
    }
}

impl MValCodec for SafetyLevel {
    fn thrift_type() -> TType {
        TType::I32
    }

    fn from_thrift(_proto: &mut dyn TInputProtocol) -> Self {
        panic!("QueryFields should not be decoded from Thrift")
    }

    fn to_thrift(&self, proto: &mut dyn TOutputProtocol) {
        proto.write_i32(self.clone() as i32).unwrap();
    }
}

#[async_trait]
pub trait VfClient {
    async fn get_result(
        &self,
        post_ids: Vec<u64>,
        safety_level: SafetyLevel,
        for_user_id: u64,
        context: Option<TwitterContextViewer>,
    ) -> HashMap<u64, Result<Option<FilteredReason>>>;
}

View on GitHub (pinned to 24c60942c5)

Solutions

  1. Do not decode SafetyLevel from Thrift; derive it from the response/query data that produced it.
  2. If decoding is genuinely needed, implement from_thrift as proto.read_i32().unwrap().try_into() with proper error handling.
  3. Split the codec trait into read-only/write-only capabilities so the compiler prevents this call.

Example fix

// before
fn from_thrift(_proto: &mut dyn TInputProtocol) -> Self {
    panic!("QueryFields should not be decoded from Thrift")
}

// after (if decoding becomes necessary)
fn from_thrift(proto: &mut dyn TInputProtocol) -> Self {
    let raw = proto.read_i32().unwrap();
    SafetyLevel::try_from(raw).unwrap_or(SafetyLevel::default())
}
Defensive patterns

Strategy: type-guard

Type guard

trait ReadCodec { fn from_thrift(p: &mut dyn TInputProtocol) -> Self; }
// SafetyLevel lacks ReadCodec; accidental decode fails to compile

Try / catch

if std::panic::catch_unwind(|| SafetyLevel::from_thrift(proto)).is_err() { /* recompute SafetyLevel from response metadata */ }

Prevention

When it happens

Trigger: Invoking MValCodec::from_thrift on SafetyLevel — generic decode utilities, round-trip codec tests, or reading captured Thrift bytes that contain the encoded i32.

Common situations: Symmetric codec test suites; refactors that decode previously write-only values; logging/replay tooling that deserializes emitted payloads.

Related errors


AI-assisted analysis of xai-org/x-algorithm@24c60942c5 (2026-08-28). Data as JSON: /api/errors/0248038bd346b696. Report an issue: GitHub.