{"record":{"id":"b016b8f5029562c4","repo":"quickwit-oss/quickwit","slug":"listing-objects-is-not-supported-for-storage","errorCode":null,"errorMessage":"listing objects is not supported for storage `{}`","messagePattern":"listing objects is not supported for storage `(.+?)`","errorType":"exception","errorClass":"StorageError","httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-storage/src/storage.rs","lineNumber":152,"sourceCode":"    async fn get_all(&self, path: &Path) -> StorageResult<OwnedBytes>;\n\n    /// Deletes a file.\n    ///\n    /// This method should return Ok(()) if the file did not exist.\n    async fn delete(&self, path: &Path) -> StorageResult<()>;\n\n    /// Deletes multiple files at once.\n    ///\n    /// The implementation may call `[`Storage::delete`] in a loop if the underlying storage does\n    /// not support deleting objects in bulk. The request can fail partially, i.e. some objects are\n    /// successfully deleted while others are not.\n    async fn bulk_delete<'a>(&self, paths: &[&'a Path]) -> Result<(), BulkDeleteError>;\n\n    /// Lists object metadata for objects whose paths start with `prefix`.\n    ///\n    /// Returned paths are relative to this storage root, like every other [`Storage`] operation.\n    fn list(&self, _prefix: &Path) -> ListObjectsStream {\n        let err = anyhow::anyhow!(\n            \"listing objects is not supported for storage `{}`\",\n            self.uri(),\n        );\n        let storage_error = StorageErrorKind::Internal.with_error(err);\n        stream::once(async move { Err(storage_error) }).boxed()\n    }\n\n    /// Returns whether a file exists or not.\n    async fn exists(&self, path: &Path) -> StorageResult<bool> {\n        match self.file_num_bytes(path).await {\n            Ok(_) => Ok(true),\n            Err(storage_err) if storage_err.kind() == StorageErrorKind::NotFound => Ok(false),\n            Err(other_storage_err) => Err(other_storage_err),\n        }\n    }\n\n    /// Returns a file size.\n    async fn file_num_bytes(&self, path: &Path) -> StorageResult<u64>;","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-storage/src/storage.rs#L134-L170","documentation":"The `Storage` trait's default `list` implementation returns a stream containing a single Internal error stating that listing is not supported for that storage type. Storages that cannot enumerate objects (historically e.g. certain specialized backends) inherit this default instead of overriding it.","triggerScenarios":"Calling `Storage::list` (or anything built on it: delete with prefix, garbage collection, merge listing) on a storage implementation that does not override the default `list` method.","commonSituations":"Using a custom or minimal Storage implementation in tests/tools that never implemented listing; invoking garbage-collection or bulk-delete flows against such a storage; writing generic code that assumes all storages support listing.","solutions":["Use a storage backend that implements `list` (local FS, S3-compatible, RAM) for listing-dependent operations","If you own the storage implementation, override `fn list(&self, prefix) -> ListObjectsStream` to stream real objects","Restructure the calling code to avoid prefix listing when the backend cannot enumerate (track object paths explicitly)","Treat the StorageErrorKind::Internal from the default method as an unsupported-operation signal in generic code"],"exampleFix":"// before (custom storage missing list)\nimpl Storage for MyStorage { /* no list() */ }\n// after\nfn list(&self, prefix: &Path) -> ListObjectsStream {\n    stream::iter(self.entries.keys().filter(...).cloned().map(Ok)).boxed()\n}","handlingStrategy":"type-guard","validationCode":"// probe support before bulk operations\nmatch storage.list(&probe_prefix).try_next().await {\n    Err(e) if e.kind() == StorageErrorKind::Internal => fallback_to_no_listing(),\n    _ => proceed_with_listing(),\n}","typeGuard":null,"tryCatchPattern":"match storage.list(&prefix).try_collect::<Vec<_>>().await {\n    Ok(objs) => use(objs),\n    Err(e) if e.kind() == StorageErrorKind::Internal => fallback_no_listing_backend(),\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Prefer backends with full list support (local FS, S3-compatible, RAM) for GC/bulk-delete flows","Override the default `list` in any custom Storage implementation","Document listing support in custom storage wrappers so callers know the limitation","Gate listing-dependent maintenance jobs on backend capability checks"],"tags":["storage","listing","unsupported","trait-default"],"backgroundTag":"unsupported-operation","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}