flxzt/rnote · error · anyhow::Error
engine snapshot does not contain 'stroke_components'.
Error message
engine snapshot does not contain 'stroke_components'.
What it means
Thrown during the maj0min13 -> maj0min15 migration when the engine snapshot object exists but has no `stroke_components` key. The migration iterates all stroke components to transform their values, so the key is mandatory in a valid snapshot.
Solutions
- Inspect the snapshot JSON in the .rnote archive and add an empty `"stroke_components": []` array if the file otherwise looks valid
- Re-save or re-export the document from the rnote version that produced it
- In code, insert the key with an empty array when missing instead of failing
Example fix
// before
for comp in engine_snapshot
.get_mut("stroke_components")
.ok_or(anyhow!("engine snapshot does not contain 'stroke_components'."))?;
// after
let comps = engine_snapshot
.entry("stroke_components")
.or_insert_with(|| ijson::IArray::new().into())
.as_array_mut()
.ok_or(anyhow!("stroke components is not a JSON array."))?; Defensive patterns
Strategy: validation
Validate before calling
if engine_snapshot.get("stroke_components").is_none() {
return Err(anyhow!("refusing migration: snapshot missing 'stroke_components'"));
} Type guard
fn has_stroke_components(snapshot: &ijson::IObject) -> bool {
snapshot.get("stroke_components").is_some()
} Try / catch
// handle the missing key explicitly on conversion
match RnoteFileMaj0Min15::try_from(file_maj0min13) {
Ok(f) => f,
Err(e) if e.to_string().contains("stroke_components") => {
// offer recovery: insert empty array and retry, or fail with clear message
}
Err(e) => return Err(e),
} Prevention
- Validate snapshot schema (stroke_components present) before migrating
- Only migrate files produced by the matching rnote version
- Insert `"stroke_components": []` for empty documents rather than omitting the key
When it happens
Trigger: Converting an RnoteFileMaj0Min13 whose engine_snapshot object lacks `stroke_components` — a snapshot written by a different schema version, a manually built object, or a file that was damaged/truncated.
Common situations: Opening .rnote files from third-party tools or very early builds that used a different snapshot schema; test fixtures with minimal snapshots; corruption during file transfer.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- document has no value `layout`.
- engine snapshot is not a JSON object.
- stroke components is not a JSON array.
- stroke component is not a JSON object.
- stroke component does not contain 'value'.
AI-assisted analysis of flxzt/rnote@bbc5354502 (2026-09-08).
Data as JSON: /api/errors/25e7caa02e21d464.
Report an issue: GitHub.
Appendix: source
Thrown at crates/rnote-engine/src/fileformats/rnoteformat/maj0min15.rs:25
#[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;
}
if let Some(shapestroke) = value
.as_object_mut()
.ok_or(anyhow!("value is not a JSON object."))?
.get_mut("shapestroke")View on GitHub (pinned to bbc5354502)