Symptom
A website generation can record its assets as uploaded=false / cid=NONE in the generation manifest even though the asset bytes successfully reached IPFS (the website-assets bucket has the object, and its ETag is a valid CID).
Evidence
For tag 0930ce37… ("Real Estate"), the decrypted website manifest shows one completed generation (fade3e5f…, 2026‑03‑05) with uploadedAssets=0/3 and all three assets uploaded=false, cid=NONE. The same three files are present in website-assets/Real_Estate/ with CIDs. So the upload succeeded server-side but the success was not persisted onto the generation's assets.
Likely cause (to confirm)
In WebsiteService._uploadPhase (lib/core/services/website_service.dart ~480):
final cid = await _uploadAssetUnencrypted(asset.localPath, asset.fileName, websiteName);
asset.cid = cid;
asset.gatewayUrl = await _buildGatewayUrl(cid); // if THIS throws, catch sets uploaded=false
asset.uploaded = true;
_uploadAssetUnencrypted throws if the PUT response has no/empty etag header ("no CID returned in etag header") — but the object IS created server-side and later has a CID. So a missing/late ETag on the PUT response (or a throw in _buildGatewayUrl) records the asset as failed while it actually succeeded. Worth confirming whether the gateway sometimes returns 200/201 without the ETag header for these PUTs.
Impact
Fix direction
Make the upload outcome durable: treat a created object as uploaded even if the ETag is missing from the PUT response (e.g., HEAD the object to fetch the CID, or re-derive it), and only mark uploaded=false on a genuine failure. Ships in a native release.
(Web-side workaround already deployed via #44 — this is the underlying server/native correctness fix.)
Symptom
A website generation can record its assets as
uploaded=false/cid=NONEin the generation manifest even though the asset bytes successfully reached IPFS (thewebsite-assetsbucket has the object, and itsETagis a valid CID).Evidence
For tag
0930ce37…("Real Estate"), the decrypted website manifest shows one completed generation (fade3e5f…, 2026‑03‑05) withuploadedAssets=0/3and all three assetsuploaded=false,cid=NONE. The same three files are present inwebsite-assets/Real_Estate/with CIDs. So the upload succeeded server-side but the success was not persisted onto the generation's assets.Likely cause (to confirm)
In
WebsiteService._uploadPhase(lib/core/services/website_service.dart~480):_uploadAssetUnencryptedthrows if the PUT response has no/emptyetagheader ("no CID returned in etag header") — but the object IS created server-side and later has a CID. So a missing/late ETag on the PUT response (or a throw in_buildGatewayUrl) records the asset as failed while it actually succeeded. Worth confirming whether the gateway sometimes returns 200/201 without the ETag header for these PUTs.Impact
website-assetsdirectly), so web shows "on a device".Fix direction
Make the upload outcome durable: treat a created object as uploaded even if the ETag is missing from the PUT response (e.g., HEAD the object to fetch the CID, or re-derive it), and only mark
uploaded=falseon a genuine failure. Ships in a native release.(Web-side workaround already deployed via #44 — this is the underlying server/native correctness fix.)