can1357/oh-my-pi · error
Cannot delete an invalid Anthropic file handle
Error message
Cannot delete an invalid Anthropic file handle
What it means
`delete` performs a DELETE against the Anthropic Files API using `handle.id`, so it first verifies the handle actually belongs to this provider and carries a file id. If `handle.provider !== "anthropic"` or `handle.id` is falsy, the handle was not produced by this client (or was corrupted/hand-built), and calling DELETE with a missing id would hit the collection URL instead of a file resource — so the client throws a fail-fast validation error instead.
Source
Thrown at packages/coding-agent/src/blob-broker/provider-files-anthropic.ts:135
body: form,
signal: request.signal,
}),
"upload",
);
const metadata = parseMetadata(await response.json());
const deleteUrl = `${ANTHROPIC_FILES_URL}/${encodeURIComponent(metadata.id)}`;
return {
provider: "anthropic",
id: metadata.id,
mimeType: metadata.mime_type,
bytes: metadata.size_bytes,
expiresAt: parseExpiresAt(metadata.expires_at),
delete: { method: "DELETE", url: deleteUrl, headers },
};
},
async delete(handle: ProviderFileHandle): Promise<void> {
if (handle.provider !== "anthropic" || !handle.id)
throw new Error("Cannot delete an invalid Anthropic file handle");
await expectAnthropicOk(
await fetchImpl(`${ANTHROPIC_FILES_URL}/${encodeURIComponent(handle.id)}`, {
method: "DELETE",
headers,
}),
"delete",
);
},
};
}
View on GitHub (pinned to 9690622007)
Solutions
- Inspect the handle just before deletion: confirm `handle.provider === "anthropic"` and `typeof handle.id === "string" && handle.id.length > 0`.
- Only pass handles that came from this client's `upload()` return value — do not build handles manually or pass handles from a different provider client.
- If handles are persisted, store the full object including `id` and `provider`, and validate on load before attempting cleanup.
- Add a migration/repair step for stored handles created before a shape change, or discard unrepairable handles and skip their deletion (the remote file will expire per its TTL).
Example fix
// before — deleting whatever handle is in the session
await anthropicClient.delete(session.fileHandle);
// after — validate the handle first
const handle = session.fileHandle;
if (handle?.provider === "anthropic" && typeof handle.id === "string" && handle.id) {
await anthropicClient.delete(handle);
} else {
logger.warn("Skipping cleanup: invalid or non-Anthropic file handle", { handle });
} Defensive patterns
Strategy: type-guard
Validate before calling
// validate handles before cleanup, especially if restored from storage
function isDeletableAnthropicHandle(h: unknown): h is { provider: "anthropic"; id: string } {
if (typeof h !== "object" || h === null) return false;
const r = h as Record<string, unknown>;
return r.provider === "anthropic" && typeof r.id === "string" && r.id.length > 0;
} Type guard
const isAnthropicFileHandle = (h: ProviderFileHandle): h is ProviderFileHandle & { provider: "anthropic"; id: string } => h.provider === "anthropic" && typeof h.id === "string" && h.id.length > 0; Try / catch
try {
await anthropicFiles.delete(handle);
} catch (err) {
if (err instanceof Error && err.message.includes("invalid Anthropic file handle")) {
logger.warn("Skipping cleanup of malformed file handle", { provider: handle?.provider });
// remote file will expire via TTL; do not crash the cleanup loop
} else throw err;
} Prevention
- Always pass handles obtained from the same provider client's upload(); never mix providers or build handles by hand.
- Persist handles in full (provider + id) and validate on restore before scheduling deletion.
- Make cleanup loops skip-and-log on invalid handles rather than letting one bad handle abort the rest.
- After any handle-shape refactor, add a migration check for previously stored handles.
When it happens
Trigger: `delete(handle)` is called with a handle whose `provider` field is not "anthropic" (e.g. a gemini or generic handle), or a handle object missing `id` — typically a handle built by hand, deserialized from persisted state with fields lost, or passed to the wrong provider's client.
Common situations: Mixing handles across provider clients (Gemini handle passed to the Anthropic client or vice versa); restoring handles from a database/JSON session where `id` was never persisted or renamed; constructing `{ provider: "anthropic" }` manually without the id; a refactor changed the handle shape and old stored handles no longer match.
Related errors
- anthropic-messages: ${data.summary}
- Anthropic thinking budget requires max_tokens greater than $
- Anthropic file upload returned an invalid expiration time
- Anthropic Files API credential is required
- Unsupported language '{value}'. Supported: {}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/c7c978c59bd2a041.
Report an issue: GitHub.