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

  1. Inspect the handle just before deletion: confirm `handle.provider === "anthropic"` and `typeof handle.id === "string" && handle.id.length > 0`.
  2. 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.
  3. If handles are persisted, store the full object including `id` and `provider`, and validate on load before attempting cleanup.
  4. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/c7c978c59bd2a041. Report an issue: GitHub.