{"record":{"id":"76e97432d296651d","repo":"quickwit-oss/quickwit","slug":"dynamic-message-storage-err-source-to-string-co","errorCode":null,"errorMessage":"dynamic message: storage_err.source.to_string() converted into io::Error (NotFound or Other depending on StorageErrorKind)","messagePattern":"dynamic message: storage_err\\.source\\.to_string\\(\\) converted into io::Error \\(NotFound or Other depending on StorageErrorKind\\)","errorType":"exception","errorClass":"io::Error (from StorageError)","httpStatus":null,"severity":"warning","filePath":"quickwit/quickwit-storage/src/error.rs","lineNumber":87,"sourceCode":"impl StorageErrorKind {\n    /// Creates a StorageError.\n    pub fn with_error(self, source: impl Into<anyhow::Error>) -> StorageError {\n        StorageError {\n            kind: self,\n            source: Arc::new(source.into()),\n            retry_after: None,\n        }\n    }\n}\n\nimpl From<StorageError> for io::Error {\n    fn from(storage_err: StorageError) -> Self {\n        let io_error_kind = match storage_err.kind() {\n            StorageErrorKind::NotFound => io::ErrorKind::NotFound,\n            _ => io::ErrorKind::Other,\n        };\n        // TODO: This is swallowing the context of the source error.\n        io::Error::new(io_error_kind, storage_err.source.to_string())\n    }\n}\n\n/// Generic StorageError.\n#[derive(Debug, Clone, Error)]\n#[error(\"storage error(kind={kind:?}, source={source})\")]\n#[allow(missing_docs)]\npub struct StorageError {\n    pub kind: StorageErrorKind,\n    #[source]\n    source: Arc<anyhow::Error>,\n    /// Server-suggested delay before the next retry, if provided (e.g. from `x-amz-retry-after`).\n    pub(crate) retry_after: Option<Duration>,\n}\n\n/// Generic Result type for storage operations.\npub type StorageResult<T> = Result<T, StorageError>;\n","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-storage/src/error.rs#L69-L105","documentation":"`From<StorageError> for io::Error` maps a storage failure into the standard library error type: NotFound keeps io::ErrorKind::NotFound, every other kind becomes Other, and the error text is just the source error's stringified message. As the in-code TODO notes, the original structured context (kind, source chain) is swallowed, so callers relying on downcasting lose detail.","triggerScenarios":"Any code path that converts a StorageError into io::Error — e.g. a reader/writer expecting std::io errors receiving a failure from an S3/Azure/GCS/Local storage backend, with the storage kind not being NotFound.","commonSituations":"Object storage outages, missing credentials/expired tokens surfacing as Other-kind io errors, network failures during split reads, file permissions issues on local storage — all collapsed to a plain message string.","solutions":["Inspect the io error's Display string to recover the underlying storage message.","Match on the io::ErrorKind only as NotFound vs other — do not expect finer kinds through this conversion.","Preserve the original StorageError in your own error type instead of converting early, so kind() and source() stay available.","If the swallowed context matters for debugging, check storage backend logs/metrics at the time of the failure."],"exampleFix":"// before\nlet io_err = io::Error::from(storage_err);\n// after\nlet io_err = io::Error::new(io_err.kind(), storage_err); // keep original error where the API permits\n// or better: propagate StorageError instead of converting","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"fn is_storage_not_found(err: &std::io::Error) -> bool {\n    err.kind() == std::io::ErrorKind::NotFound\n}","tryCatchPattern":"match operation().await {\n    Err(e) if e.kind() == std::io::ErrorKind::NotFound => handle_missing(),\n    Err(other) => {\n        // kind is Other; the Display string is the only remaining storage detail\n        log::warn!(\"storage failure: {other}\");\n    }\n    Ok(v) => v,\n}","preventionTips":["Preserve StorageError end-to-end instead of converting to io::Error early.","Do not rely on fine-grained io::ErrorKind through this conversion — only NotFound is preserved.","Check storage backend logs/credentials when you see Other-kind io errors."],"tags":["storage","io","error-conversion","context-loss"],"backgroundTag":"file-read-failed","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}