HeyPuter/puter · error · HttpError
bad_request
bad_request
Error message
No ETag for ${filePath} part ${partNumber} What it means
AppDriver.read resolves { uid, id } against the store; a null result means no app matched either key. Thrown as 404 not_found after actor resolution but before access checks, so it does not leak whether an app exists that the caller can't read. Both uid and id are accepted lookup keys.
Source
Thrown at src/backend/clients/s3/S3Client.ts:366
if (bytesRead <= 0) break;
const body =
bytesRead === partBuffer.length
? partBuffer
: partBuffer.subarray(0, bytesRead);
const { ETag } = await client.send(
new UploadPartCommand({
Bucket: bucket,
ContentLength: bytesRead,
Key: key,
PartNumber: partNumber,
UploadId,
Body: body,
}),
);
if (!ETag)
throw new HttpError(
400,
`No ETag for ${filePath} part ${partNumber}`,
{ legacyCode: 'bad_request' },
);
uploadedParts.push({ ETag, PartNumber: partNumber });
offset += bytesRead;
partNumber++;
}
await client.send(
new CompleteMultipartUploadCommand({
Bucket: bucket,
Key: key,
UploadId,
MultipartUpload: { Parts: uploadedParts },
}),
);View on GitHub (pinned to 908ec23eda)
Solutions
- Verify the uid/id source — confirm it came from a prior create()/list() in the same environment.
- Treat 404 as 'not found or deleted' and refresh the client's app list.
- Validate uid format (UUID-ish) before calling to fail fast on garbage.
- If the app was deleted, offer to re-create rather than silently looping.
Example fix
// before
const app = await puter.apps.get(staleUid);
// after — handle not-found explicitly
let app;
try { app = await puter.apps.get(storedUid); }
catch (e) {
if (e?.code === 'not_found') { refreshAppList(); return; }
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// Fail fast on malformed uids before the round trip.
function looksLikeUid(v) { return typeof v === 'string' && /^[A-Za-z0-9_-]{8,}$/.test(v); }
if (!looksLikeUid(storedUid)) { refreshAppList(); return; } Try / catch
try { return await puter.apps.get(storedUid); }
catch (e) {
if (e?.code === 'not_found') { refreshAppList(); notify('App not found.'); return null; }
throw e;
} Prevention
- Source uids only from prior create()/list() results in the same environment.
- Refresh cached uids when a 404 occurs rather than retrying the same value.
- Do not mix dev and prod uids.
When it happens
Trigger: Calling puter.apps.get(uid) or get(id) with a value that matches no app row — typo, deleted app, wrong environment (dev uid sent to prod), or a uid copied from a different object type.
Common situations: Stale uid cached client-side after the app was deleted; cross-environment uid (test → prod); user pasted a file uid instead of an app uid; id confusion (numeric id vs uid string).
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/2749f662153baf17.
Report an issue: GitHub.