{"record":{"id":"f3e7955dcc2fef85","repo":"neondatabase/neon","slug":"missing-size-content-length-header","errorCode":null,"errorMessage":"Missing size (content length) header","messagePattern":"Missing size \\(content length\\) header","errorType":"exception","errorClass":"DownloadError","httpStatus":null,"severity":"error","filePath":"libs/remote_storage/src/gcs_bucket.rs","lineNumber":1364,"sourceCode":"       Ok(())\n    }\n    async fn head_object(\n        &self,\n        key: &RemotePath,\n        cancel: &CancellationToken,\n    ) -> Result<ListingObject, DownloadError> {\n        let path = self\n            .relative_path_to_gcs_object(key)\n            .trim_start_matches(\"/\")\n            .to_string();\n\n        let resp = self.head_object(path.clone(), cancel).await?;\n\n        let last_modified: SystemTime = to_system_time(resp.updated).unwrap_or(SystemTime::now());\n\n        let Some(size) = resp.size else {\n            return Err(DownloadError::Other(anyhow::anyhow!(\n                \"Missing size (content length) header\"\n            )));\n        };\n\n        Ok(ListingObject {\n            key: self.gcs_object_to_relative_path(&path),\n            last_modified,\n            size: size as u64,\n        })\n    }\n\n    async fn list_versions(\n        &self,\n        prefix: Option<&RemotePath>,\n        mode: ListingMode,\n        max_keys: Option<NonZeroU32>,\n        cancel: &CancellationToken,\n    ) -> Result<crate::VersionListing, DownloadError> {\n        let kind = RequestKind::ListVersions;","sourceCodeStart":1346,"sourceCodeEnd":1382,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/libs/remote_storage/src/gcs_bucket.rs#L1346-L1382","documentation":"Building a ListingObject from a GCS object-metadata response requires the object's size; the parsed JSON had no size field, so listing construction fails. GCS always reports size for existing objects, so absence indicates an anomalous response (a gateway/emulator dropping the field, or an API shape change) rather than normal operation.","triggerScenarios":"list_objects_from_head via head_object succeeding HTTP-wise but returning a JSON body without size — typically a GCS-compatible intermediary, or an error body that parsed as an object.","commonSituations":"S3-compatible fronts for GCS dropping size in metadata; emulators in local dev; rare malformed responses.","solutions":["Log the raw head response body to confirm size is truly absent versus a parse mismatch","Compare responses from real GCS and any intermediary in the request path","Retry once for transient anomalies","Fix the intermediary's field mapping or bypass it"],"exampleFix":"// before: absent size aborts listing construction\nlet Some(size) = resp.size else {\n    return Err(DownloadError::Other(anyhow::anyhow!(\"Missing size (content length) header\")));\n};\n\n// after: fall back to a separate stat when the head body lacks size\nlet size = match resp.size {\n    Some(s) => s,\n    None => self.stat_object(&key, cancel).await?.size,\n};","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Anomalous metadata: isolate the failing key, keep the rest of the listing.\nlet objects: Vec<ListingObject> = futures::stream::iter(keys)\n    .filter_map(|key| async move {\n        match storage.list_objects_from_head(key.clone(), &cancel).await {\n            Ok(obj) => Some(obj),\n            Err(DownloadError::Other(e)) if format!(\"{e:#}\").contains(\"Missing size\") => {\n                tracing::error!(\"metadata for {key} lacks size; skipping: {e:#}\");\n                None\n            }\n            Err(e) => { tracing::error!(\"head failed for {key}: {e:#}\"); None }\n        }\n    })\n    .collect()\n    .await;","preventionTips":["Regression-test listing code against the exact gateway/emulator used in production","Log raw metadata bodies (debug) when integrating a new storage endpoint","Treat single-missing-field errors as canaries for response-shape drift, investigate the intermediary"],"tags":["gcs","google-cloud-storage","metadata","listing","deserialization"],"backgroundTag":"missing-response-field","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}