diem/diem · error

Not allowed to assume update invariant

Error message

Not allowed to assume update invariant

What it means

A safety check in assert_or_assume_translated_invariants: update invariants (GlobalInvariantUpdate) may only be asserted, never assumed, because assuming an update invariant would be unsound (it describes state changes, not state). If the pass would emit an Assume of an update invariant, it panics instead.

Source

Thrown at language/move-prover/bytecode/src/global_invariant_instrumentation_v2.rs:666

    /// xlated_invariants that is also in inv_set at the current location,
    fn assert_or_assume_translated_invariants(
        &mut self,
        xlated_invariants: &[(Loc, GlobalId, Exp)],
        inv_set: &BTreeSet<GlobalId>,
        prop_kind: PropKind,
    ) {
        let global_env = self.builder.global_env();
        for (loc, mid, cond) in xlated_invariants {
            if inv_set.contains(mid) {
                // Check for hard-to-debug coding error (this is not a user error)
                if inv_set.contains(mid)
                    && matches!(prop_kind, PropKind::Assume)
                    && matches!(
                        global_env.get_global_invariant(*mid).unwrap().kind,
                        ConditionKind::GlobalInvariantUpdate(..)
                    )
                {
                    panic!("Not allowed to assume update invariant");
                }
                self.emit_invariant(loc, cond, prop_kind);
            }
        }
    }

    /// Emit an assert or assume for one invariant, give location and expression for the property
    fn emit_invariant(&mut self, loc: &Loc, cond: &Exp, prop_kind: PropKind) {
        self.builder.set_next_debug_comment(format!(
            "global invariant {}",
            loc.display(self.builder.global_env())
        ));
        // No error messages on assumes
        if prop_kind == PropKind::Assert {
            self.builder
                .set_loc_and_vc_info(loc.clone(), GLOBAL_INVARIANT_FAILS_MESSAGE);
        }
        self.builder

View on GitHub (pinned to fc4714a8ea)

Solutions

  1. Use unmodified v2 instrumentation logic, which only assumes plain global invariants
  2. If customizing the pass, filter GlobalInvariantUpdate invariants out of Assume sets before calling emit_invariant
  3. Report upstream with the Move function/spec that reproduces it

Example fix

// before: assuming update invariant
let kind = PropKind::Assume;
// after: only assert update invariants
let kind = if is_update_inv { PropKind::Assert } else { PropKind::Assume };
Defensive patterns

Strategy: validation

Validate before calling

fn safe_prop_kind(inv_kind: &ConditionKind, desired: PropKind) -> PropKind {
    if matches!(inv_kind, ConditionKind::GlobalInvariantUpdate(_)) { PropKind::Assert } else { desired }
}

Type guard

fn is_update_invariant(kind: &ConditionKind) -> bool {
    matches!(kind, ConditionKind::GlobalInvariantUpdate(_))
}

Try / catch

// in custom emit logic:
if is_update_invariant(&inv.kind) && matches!(kind, PropKind::Assume) {
    eprintln!("refusing to assume update invariant; emitting assert instead");
}

Prevention

When it happens

Trigger: A translated invariant set marked as PropKind::Assume contains an update invariant — arising from a bug or modification in how invariants are translated/assigned prop kinds for a bytecode (e.g. around opaque calls or reads where update invariants should only be asserted).

Common situations: Patched instrumentation logic emitting assumes at points the original design only asserts; spec-language changes letting update invariants become applicable at assume points.

Related errors


AI-assisted analysis of diem/diem@fc4714a8ea (2026-09-04). Data as JSON: /api/errors/fa995f74f82c9aa9. Report an issue: GitHub.