flxzt/rnote · error · anyhow::Error
no value `stroke_components` in `store_snapshot`
Error message
no value `stroke_components` in `store_snapshot`
What it means
Thrown by the TryFrom<RnoteFileMaj0Min5Patch8> migration into RnoteFileMaj0Min5Patch9 when the store_snapshot JSON object has no "stroke_components" key. The migration must rewrite each stroke component's inner representation, so the key is mandatory in this version's snapshot schema.
Solutions
- Add a "stroke_components": [] JSON array to store_snapshot in the file (empty array is accepted).
- Open/save the file once in the older app version that writes this key, then retry migration.
- Restore the document from a backup that contains the complete store_snapshot.
Example fix
// before (store_snapshot)
{ "image_snapshots": [] }
// after
{ "image_snapshots": [], "stroke_components": [] } Defensive patterns
Strategy: validation
Validate before calling
let snap = file_json.get("store_snapshot").and_then(|v| v.as_object())?;
if !snap.contains_key("stroke_components") {
return Err("file predates maj0-min5-patch9 schema: missing stroke_components".into());
} Type guard
fn has_stroke_components(v: &serde_json::Value) -> bool {
v.get("store_snapshot")
.and_then(|s| s.get("stroke_components"))
.map(|c| c.is_array())
.unwrap_or(false)
} Try / catch
// Rust caller migrating files
match RnoteFileMaj0Min5Patch9::try_from(file_maj0min5patch8) {
Ok(f) => /* proceed */,
Err(e) if e.to_string().contains("stroke_components") => {
// re-save with the older app version or restore backup
}
} Prevention
- Detect file version before migrating and warn on schema gaps.
- Keep old .rnote files backed up before opening with newer app versions.
- Test migrations against archived files from every historical format version.
When it happens
Trigger: Opening an older .rnote file whose store_snapshot object lacks "stroke_components" entirely when the loader runs the maj0-min5-patch9 upgrade step.
Common situations: Very old or pre-release .rnote files with a different snapshot schema, files stripped by external tools, or hand-edited documents.
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
- bitmapimage does not contain 'image'.
- image does not contain 'rectangle'.
- bitmapimage does not contain 'rectangle'.
- rect does not contain 'transform'.
- transform does not contain 'affine'.
AI-assisted analysis of flxzt/rnote@bbc5354502 (2026-09-08).
Data as JSON: /api/errors/39505b0ea6252e76.
Report an issue: GitHub.
Appendix: source
Thrown at crates/rnote-engine/src/fileformats/rnoteformat/maj0min5patch9.rs:26
/// Rnote file in version: maj 0 min 5 patch 9.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct RnoteFileMaj0Min5Patch9 {
/// The document.
#[serde(rename = "document", alias = "sheet")]
pub(crate) document: ijson::IValue,
/// The snapshot of the store.
#[serde(rename = "store_snapshot")]
pub(crate) store_snapshot: ijson::IValue,
}
impl TryFrom<RnoteFileMaj0Min5Patch8> for RnoteFileMaj0Min5Patch9 {
type Error = anyhow::Error;
fn try_from(mut file: RnoteFileMaj0Min5Patch8) -> Result<RnoteFileMaj0Min5Patch9, Self::Error> {
let stroke_components = file
.store_snapshot
.get_mut("stroke_components")
.ok_or_else(|| anyhow!("no value `stroke_components` in `store_snapshot`"))?
.as_array_mut()
.ok_or_else(|| anyhow!("value `stroke_components` is not a JSON array."))?;
for value in stroke_components {
let stroke = value
.as_object_mut()
.ok_or_else(|| anyhow!("value in `stroke_components` array is not a JSON Object."))?
.get_mut("value")
.ok_or_else(|| {
anyhow!("no value `value` in JSON object of `stroke_components` array.")
})?;
if stroke.is_null() {
continue;
}
if let Some(brushstroke) = stroke
.as_object_mut()View on GitHub (pinned to bbc5354502)