{"record":{"id":"0dfcc11f8793f11f","repo":"neondatabase/neon","slug":"no-items-returned","errorCode":null,"errorMessage":"no items returned","messagePattern":"no items returned","errorType":"exception","errorClass":"DownloadError","httpStatus":null,"severity":"error","filePath":"libs/remote_storage/src/gcs_bucket.rs","lineNumber":277,"sourceCode":"                    warn_threshold,\n                    max_retries,\n                    \"listing object versions\",\n                    cancel,\n                ) \n                .await\n                .ok_or_else(|| DownloadError::Cancelled)\n                .and_then(|x| x)?;\n                \n            let res = response.json::<GCSListResponse>()\n                .await\n                .map_err(|e| DownloadError::Other(e.into()))?;\n                    \n            // fill up our results vec, \n            continuation_token = res.next_page_token;\n            \n            let version_listing = \n                res.items\n                    .ok_or_else(|| DownloadError::Other(anyhow::anyhow!(\"no items returned\")))?\n                    .into_iter()\n                    .map(| GCSObject { name, updated, time_deleted, generation, .. } | {\n                        // don't `filter_map`, a `None` for `last_modified` ('updated') is bad for\n                        // time travel, so catch it.\n                        if updated.is_none() {\n                           return Err(\n                               DownloadError::Other(\n                                   anyhow::anyhow!(\"no 'updated' field\")\n                               )\n                           )\n                        }\n                        Ok(\n                            GCSVersion {\n                                key: self.gcs_object_to_relative_path(&name),\n                                last_modified: to_system_time(updated).unwrap(),\n                                id: VersionId(generation.expect(\"no version id\")),\n                                time_deleted: to_system_time(time_deleted),\n                            }","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/libs/remote_storage/src/gcs_bucket.rs#L259-L295","documentation":"While paginating the GCS JSON API list-objects-with-versions response, the parsed GCSListResponse had no items field and this code treats that as an error. The GCS JSON API legitimately omits items when a page contains zero matching objects (empty bucket/prefix, or a trailing empty page), so this usually fires on a valid empty listing rather than a real protocol failure.","triggerScenarios":"Calling list_versions/list with a prefix that matches zero objects; listing an empty bucket; a final pagination round-trip whose continuation token yields no items; wrong bucket name so nothing matches.","commonSituations":"Tenants/prefixes with no uploaded objects yet; typo'd prefix; time travel against a freshly created bucket. Any empty result set turns into DownloadError::Other(\"no items returned\") instead of an empty list.","solutions":["Fix at the call site or upstream: treat missing items as an empty page — res.items.unwrap_or_default() instead of ok_or_else(...)","Verify the bucket name and prefix actually contain objects before calling","If data is expected, confirm objects were uploaded with the exact key prefix being listed","Add a preflight check (a single-page list without continuation) to distinguish 'empty' from 'error'"],"exampleFix":"// before: empty page -> hard error\nlet version_listing = res.items\n    .ok_or_else(|| DownloadError::Other(anyhow::anyhow!(\"no items returned\")))?\n    .into_iter()\n    .map(/* ... */);\n\n// after: empty page -> empty page of results, loop exits via absent continuation token\nlet version_listing = res.items\n    .unwrap_or_default()\n    .into_iter()\n    .map(/* ... */);","handlingStrategy":"fallback","validationCode":"// Confirm the prefix actually has objects before relying on the listing result.\nasync fn prefix_populated(storage: &GenericRemoteStorage, prefix: &RemotePath, cancel: &CancellationToken) -> bool {\n    matches!(storage.list_files(Some(prefix), ListingMode::NoDelimiter, cancel).await, Ok(files) if !files.is_empty())\n}","typeGuard":null,"tryCatchPattern":"// GCS omits `items` on empty pages: treat this specific error as an empty listing.\nlet versions = match storage.list_versions(key, None, &cancel).await {\n    Ok(v) => v,\n    Err(DownloadError::Other(e)) if format!(\"{e:#}\").contains(\"no items returned\") => {\n        tracing::debug!(\"empty GCS listing for {key}\");\n        Default::default()\n    }\n    Err(e) => return Err(e),\n};","preventionTips":["Never assume an error from a listing means the service is broken — empty buckets are legal states","Verify bucket names and prefixes in configuration before first use","If you control the library, patch items to unwrap_or_default() and contribute the fix upstream"],"tags":["gcs","google-cloud-storage","listing","pagination","api-response"],"backgroundTag":"empty-list-response","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}