flxzt/rnote · error · anyhow::Error
engine snapshot is not a JSON object.
Error message
engine snapshot is not a JSON object.
What it means
Thrown when converting an RnoteFileMaj0Min13 to RnoteFileMaj0Min15 if `engine_snapshot` is not a JSON object (e.g. it is null, an array, or a scalar). The migration needs to mutate keys inside the snapshot object, so it must first downcast it to a mutable IObject.
Solutions
- Verify the .rnote file's engine_snapshot is a JSON object (unzip and inspect)
- If the file is corrupt, recover content by re-importing or re-saving from a working rnote build
- In code, check `value.engine_snapshot.is_object()` before conversion and handle null snapshots with a default snapshot
Example fix
// before
let engine_snapshot = value
.engine_snapshot
.as_object_mut()
.ok_or(anyhow!("engine snapshot is not a JSON object."))?;
// after
if value.engine_snapshot.is_null() {
value.engine_snapshot = serde_json::json!({ "stroke_components": [] }).into();
}
let engine_snapshot = value
.engine_snapshot
.as_object_mut()
.ok_or(anyhow!("engine snapshot is not a JSON object."))?; Defensive patterns
Strategy: type-guard
Validate before calling
if !value.engine_snapshot.is_object() {
return Err(anyhow!("refusing migration: engine snapshot is not a JSON object"));
} Type guard
fn snapshot_is_object(v: &ijson::IValue) -> bool {
v.as_object().is_some()
} Try / catch
// convert and map the failure to a user-facing message
let file = RnoteFileMaj0Min15::try_from(file_maj0min13)
.context("This .rnote file's engine snapshot is invalid; the file may be corrupted or from an unsupported version.")?; Prevention
- Check `engine_snapshot.is_object()` before conversion
- Treat null snapshots as empty snapshots in your loader
- Validate .rnote archives after download/transfer (checksums)
When it happens
Trigger: Calling TryFrom on an RnoteFileMaj0Min13 whose `engine_snapshot` field deserialized to a non-object JSON value — typically a null snapshot from a corrupt/older file, or a struct constructed manually with wrong data.
Common situations: Opening a damaged .rnote file where the engine snapshot failed to parse; hand-crafted test fixtures with engine_snapshot set to null; upstream format changes that moved the snapshot.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- stroke components is not a JSON array.
- stroke component is not a JSON object.
- document has no value `layout`.
- engine snapshot does not contain 'stroke_components'.
- value is not a JSON object.
AI-assisted analysis of flxzt/rnote@bbc5354502 (2026-09-08).
Data as JSON: /api/errors/022e2ff8c851b53a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs:21
use anyhow::anyhow;
use ijson::IValue;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RnoteFileMaj0Min15 {
/// A snapshot of the engine.
#[serde(rename = "engine_snapshot")]
pub engine_snapshot: ijson::IValue,
}
impl TryFrom<RnoteFileMaj0Min13> for RnoteFileMaj0Min15 {
type Error = anyhow::Error;
fn try_from(mut value: RnoteFileMaj0Min13) -> Result<Self, Self::Error> {
let engine_snapshot = value
.engine_snapshot
.as_object_mut()
.ok_or(anyhow!("engine snapshot is not a JSON object."))?;
for comp in engine_snapshot
.get_mut("stroke_components")
.ok_or(anyhow!(
"engine snapshot does not contain 'stroke_components'."
))?
.as_array_mut()
.ok_or(anyhow!("stroke components is not a JSON array."))?
.iter_mut()
{
let value = comp
.as_object_mut()
.ok_or(anyhow!("stroke component is not a JSON object."))?
.get_mut("value")
.ok_or(anyhow!("stroke component does not contain 'value'."))?;
if value.is_null() {
continue;
}View on GitHub (pinned to bbc5354502)