{"record":{"id":"89045702df2c9f41","repo":"influxdata/influxdb","slug":"restorecatalog-record-encountered-without-object-store","errorCode":null,"errorMessage":"RestoreCatalog record encountered without object-store access (sync apply path): the catalog cannot reload backup state from here — use the async apply path","messagePattern":"RestoreCatalog record encountered without object-store access \\(sync apply path\\): the catalog cannot reload backup state from here — use the async apply path","errorType":"error_code","errorClass":"FormatError","httpStatus":null,"severity":"error","filePath":"influxdb3_catalog/src/format/mod.rs","lineNumber":218,"sourceCode":"        \"record data exceeds file bounds: offset {offset}, length {length}, file size {file_size}\"\n    )]\n    RecordExceedsFile {\n        offset: u64,\n        length: u32,\n        file_size: u64,\n    },\n\n    /// File header is internally inconsistent (e.g. a reserved field is\n    /// nonzero).\n    #[error(\"invalid header: {reason}\")]\n    InvalidHeader { reason: &'static str },\n\n    /// A `RestoreCatalog` record was encountered during sync apply with an\n    /// empty preload. Callers on a persistence path must first load the\n    /// backup state off-lock via `preload_restore_for_records` /\n    /// `preload_restore_for_file` and pass the resulting `RestorePreload`\n    /// into the apply call.\n    #[error(\n        \"RestoreCatalog record encountered without object-store access \\\n         (sync apply path): the catalog cannot reload backup state from \\\n         here — use the async apply path\"\n    )]\n    RestoreRequiresStore,\n\n    /// Loading the backup snapshot/log files referenced by a `RestoreCatalog`\n    /// record failed.\n    #[error(\"failed to load restore source: {0}\")]\n    RestoreLoadFailed(String),\n\n    /// The checkpoint or log path in a `RestoreCatalog` record could not be\n    /// parsed as an object-store path.\n    #[error(\"invalid restore path: {0}\")]\n    InvalidRestorePath(String),\n\n    /// The backup snapshot referenced by a `RestoreCatalog` record was not\n    /// found at the recorded checkpoint path.","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/influxdata/influxdb/blob/06200ef96ba82c5f6727e5038a83af8e722c6875/influxdb3_catalog/src/format/mod.rs#L200-L236","documentation":"A RestoreCatalog record was being applied through the synchronous apply path while the restore preload (backup state loaded from object storage) was empty. Restoring requires reading backup snapshot/log files from the object store, which the sync path cannot do (it runs while holding the catalog lock and has no store access). The caller must preload the backup state off-lock first and pass it in.","triggerScenarios":"Calling the sync apply API (e.g. catalog.apply with sync semantics) on a record batch that includes a RestoreCatalog record without first calling preload_restore_for_records / preload_restore_for_file and supplying the resulting RestorePreload.","commonSituations":"Writing custom replay/compaction code over catalog logs and forgetting the preload step when a restore record may appear; replaying a historical log segment containing a restore operation.","solutions":["Before the sync apply, call preload_restore_for_records (or preload_restore_for_file) to load the backup state.","Pass the resulting RestorePreload into the apply call.","Or switch to the async apply path, which can access the object store directly."],"exampleFix":"// before\n// catalog.apply_in_sync(records)?;\n// after\n// let preload = catalog.preload_restore_for_records(&records)?;\n// catalog.apply_in_sync_with_preload(records, preload)?;","handlingStrategy":"try-catch","validationCode":"// Detect restore records in the batch before sync apply\nif records.iter().any(|r| matches!(r, Record::RestoreCatalog(_))) {\n    let preload = catalog.preload_restore_for_records(&records)?; // off-lock, uses store\n    return catalog.apply_with_preload(records, preload);\n}","typeGuard":null,"tryCatchPattern":"match catalog.apply_in_sync(records) {\n    Err(e) if e.to_string().contains(\"RestoreCatalog\") => {\n        let preload = catalog.preload_restore_for_records(&records)?;\n        catalog.apply_with_preload(records, preload)\n    }\n    r => r,\n}","preventionTips":["Always use the async apply path when replaying historical logs that may contain restores","Read the apply API docs: sync path requires a preloaded RestorePreload for restore records","Add a test covering logs containing RestoreCatalog records"],"tags":["restore","api-usage","catalog","sync-path"],"backgroundTag":"invalid-state-transition","analyzedSha":"06200ef96ba82c5f6727e5038a83af8e722c6875","analyzedAt":"2026-09-19T12:55:30.003Z","contentChangedAt":"2026-09-19T12:55:30.003Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}