diem/diem · error

must be non-deletion

Error message

must be non-deletion

What it means

After a successful sandbox (non-VM) publish, the code is written via a ChangeSet. Modules are extracted with changeset.into_modules(), yielding (ModuleId, Option<Vec<u8>>) where the Option is Some for published/updated modules and None for deletions. Publish only produces additions, so the code expects(blob).expect("must be non-deletion") panics with 'must be non-deletion' if a deletion (None) unexpectedly appears — indicating an internal inconsistency (a module being republished/replaced resulted in a deletion entry).

Source

Thrown at language/tools/move-cli/src/sandbox/commands/publish.rs:151

                            has_error = true;
                        }
                    }
                }
            }
        }

        if !has_error {
            let (changeset, events) = session.finish().map_err(|e| e.into_vm_status())?;
            assert!(events.is_empty());
            if verbose {
                explain_publish_changeset(&changeset, state);
            }
            let modules: Vec<_> = changeset
                .into_modules()
                .map(|(module_id, blob_opt)| {
                    let addr_name = id_to_ident[&module_id];
                    let ident = (module_id, addr_name);
                    (ident, blob_opt.expect("must be non-deletion"))
                })
                .collect();
            state.save_modules(&modules, named_address_mapping)?;
        }
    } else {
        // NOTE: the VM enforces the most strict way of module republishing and does not allow
        // backward incompatible changes, as as result, if this flag is set, we skip the VM process
        // and force the CLI to override the on-disk state directly
        let mut serialized_modules = vec![];
        for ((_, address_name_opt), module) in modules {
            let id = module.self_id();
            let mut module_bytes = vec![];
            module.serialize(&mut module_bytes)?;
            serialized_modules.push(((id, address_name_opt), module_bytes));
        }
        state.save_modules(&serialized_modules, named_address_mapping)?;
    }

View on GitHub (pinned to fc4714a8ea)

Solutions

  1. Avoid republishing the same module at the same address; bump the package version or move to a new module/address.
  2. Clear the affected module (or reset storage/) and republish cleanly from source.
  3. Ensure you consistently use one publish path (sandbox publish vs VM) for a given storage directory.
  4. If reproducible, report it — this is an invariant violation (expect) rather than a normal user-facing error path.

Example fix

// before: republish over existing module triggers deletion entry panic
$ move sandbox publish build/my_pkg   # module 0x42::app already in storage
must be non-deletion

// after: remove stale storage first
$ rm -rf storage && move sandbox publish build/my_pkg
Defensive patterns

Strategy: fallback

Validate before calling

// Detect existing modules at the same addresses before a lenient republish
for m in built_modules {
    if state.get_module(&m.id).is_some() {
        warn("module {} already published; reset storage or bump version", m.id);
    }
}

Prevention

When it happens

Trigger: Publishing over existing state where the sandbox changeset records a module deletion instead of an upsert — e.g. republishing a module under conditions the CLI's lenient republish path handles via changesets, and the changeset marks the old module for deletion.

Common situations: Republishing a module that already exists at the same address; state left in an inconsistent condition by earlier partial publishes; mixing publish modes (VM vs sandbox) on the same storage.

Related errors


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