diff --git a/.changeset/fix-media-picker-broken-image.md b/.changeset/fix-media-picker-broken-image.md
new file mode 100644
index 000000000..a6f397dba
--- /dev/null
+++ b/.changeset/fix-media-picker-broken-image.md
@@ -0,0 +1,5 @@
+---
+"@emdash-cms/admin": patch
+---
+
+Fixes broken image collapsing media picker container — adds `onError` handler and fallback placeholder so Change/Remove buttons remain accessible when referenced image is missing from storage
diff --git a/packages/admin/src/components/BlockKitMediaPickerField.tsx b/packages/admin/src/components/BlockKitMediaPickerField.tsx
index 8ee052389..febadc32c 100644
--- a/packages/admin/src/components/BlockKitMediaPickerField.tsx
+++ b/packages/admin/src/components/BlockKitMediaPickerField.tsx
@@ -1,6 +1,6 @@
import { Button } from "@cloudflare/kumo";
import { useLingui } from "@lingui/react/macro";
-import { Image as ImageIcon, X } from "@phosphor-icons/react";
+import { Image as ImageIcon, ImageBroken, X } from "@phosphor-icons/react";
import * as React from "react";
import type { MediaItem } from "../lib/api";
@@ -35,7 +35,12 @@ export function BlockKitMediaPickerField({
}: BlockKitMediaPickerFieldProps) {
const { t } = useLingui();
const [pickerOpen, setPickerOpen] = React.useState(false);
+ const [imageBroken, setImageBroken] = React.useState(false);
const url = typeof value === "string" && value.length > 0 ? value : "";
+
+ React.useEffect(() => {
+ setImageBroken(false);
+ }, [url]);
const filter = mimeTypeFilter ?? "image/";
const canPreview = isSafePreviewUrl(url);
@@ -55,30 +60,65 @@ export function BlockKitMediaPickerField({
{canPreview ? (
-
-

-
-
-
+ imageBroken ? (
+
+
+
+ {t`Image not found`}
+
+
+
+
+
+
+ ) : (
+
+

setImageBroken(true)}
+ />
+
+
+
+
-
+ )
) : (