{"record":{"id":"9d5c934e8d2c72ee","repo":"sxyazi/yazi","slug":"remote-file-has-changed-since-last-download","errorCode":null,"errorMessage":"Remote file has changed since last download","messagePattern":"Remote file has changed since last download","errorType":"validation","errorClass":"io::Error","httpStatus":null,"severity":"warning","filePath":"yazi-vfs/src/stamp.rs","lineNumber":81,"sourceCode":"\n\tfn encode(cha: Cha, url: Url) -> io::Result<Vec<u8>> {\n\t\tlet name = url.name().ok_or_else(|| io::Error::other(\"URL has no filename\"))?;\n\n\t\tlet mut buf = Vec::with_capacity(Self::SIG_LEN + name.len());\n\t\tbuf.extend_from_slice(cha.hash_u128_str(&mut [0; Self::SIG_LEN]).as_bytes());\n\t\tbuf.extend_from_slice(name.encoded_bytes());\n\n\t\tOk(buf)\n\t}\n\n\tpub fn validate(&self, cha: Cha, url: Url) -> io::Result<()> {\n\t\tlet name = url.name().ok_or_else(|| io::Error::other(\"URL has no filename\"))?;\n\t\tif self.name() != name.encoded_bytes() {\n\t\t\treturn Err(io::Error::new(io::ErrorKind::InvalidData, \"Cache stamp does not match target\"));\n\t\t}\n\n\t\tif self.sig() != cha.hash_u128_str(&mut [0; Self::SIG_LEN]) {\n\t\t\treturn Err(io::Error::new(\n\t\t\t\tio::ErrorKind::InvalidData,\n\t\t\t\t\"Remote file has changed since last download\",\n\t\t\t));\n\t\t}\n\t\tOk(())\n\t}\n\n\t#[inline]\n\tpub fn sig(&self) -> &str { unsafe { str::from_utf8_unchecked(&self.0[..Self::SIG_LEN]) } }\n\n\t#[inline]\n\tfn name(&self) -> &[u8] { &self.0[Self::SIG_LEN..] }\n}\n\nimpl TryFrom<Vec<u8>> for Stamp {\n\ttype Error = io::Error;\n\n\tfn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/sxyazi/yazi/blob/8ebf930f1796ae2076b7d6b0c5e93a1002c18134/yazi-vfs/src/stamp.rs#L63-L99","documentation":"Returned by Stamp::validate when the stored signature (self.sig()) differs from the current Cha's hash_u128_str. The cached data was valid for the target name, but the remote file's metadata/content signature has changed since the stamp was written, so the cache is stale.","triggerScenarios":"Calling stamp.validate(cha, url) after the remote file was modified, re-uploaded, or its metadata (size/mtime feeding the Cha hash) changed; also when validating with a Cha fetched at a different time than the stamp was written.","commonSituations":"Remote file updated on the server between downloads; clock/metadata skew on the remote; using a cached stamp after re-listing the remote directory shows new metadata.","solutions":["Re-download the remote file and rewrite the stamp via Stamp::write with the fresh Cha","Re-fetch current Cha metadata for the URL before validating to avoid false staleness","If staleness is acceptable, bypass validation and serve cache with a warning"],"exampleFix":"// before\nstamp.validate(cha, url)?;\n// after\nmatch stamp.validate(cha, url) {\n    Ok(()) => use_cached(),\n    Err(e) if e.to_string().contains(\"changed since last download\") => redownload_and_restamp(url).await?,\n    Err(e) => return Err(e),\n}","handlingStrategy":"validation","validationCode":"let mut sig = [0u8; 16];\nlet current = cha.hash_u128_str(&mut sig);\nif stamp.sig() != current { /* stale: re-download before serving cache */ }","typeGuard":"fn stamp_fresh(stamp: &Stamp, cha: &Cha) -> bool {\n    let mut buf = [0u8; 16];\n    stamp.sig() == cha.hash_u128_str(&mut buf)\n}","tryCatchPattern":"match stamp.validate(cha, url) {\n    Ok(()) => serve_cached(),\n    Err(e) if e.to_string().contains(\"changed since last download\") => redownload().await?,\n    Err(e) => return Err(e),\n}","preventionTips":["Re-fetch Cha metadata from the remote before each validation","Rewrite the stamp whenever the remote file changes","Expire cache entries older than a freshness threshold"],"tags":["cache","staleness","integrity","rust"],"backgroundTag":"cache-stamp-outdated","analyzedSha":"8ebf930f1796ae2076b7d6b0c5e93a1002c18134","analyzedAt":"2026-09-02T18:38:25.566Z","contentChangedAt":"2026-09-02T18:38:25.566Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}