STET

agent_patch

runs/2026-02-27__21-30-28__gpt-5-4/flux-pr-4567/flux-pr-4567.1-of-1.2026-02-27__21-30-28__gpt-5-4/agent-logs/agent.patch

6672 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-4567.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/docs/content/api.mdx b/app/packages/docs/content/api.mdx
index e673ea9..b0941df 100644
--- a/tmp/agent-patch-flux-pr-4567.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/docs/content/api.mdx
+++ b/app/packages/docs/content/api.mdx
@@ -1630,6 +1630,31 @@ z.set(z.string()).check(z.size(5)); // must contain 5 items exactly
 </Tab>
 </Tabs>
 
+## Files
+
+To validate `File` inputs:
+
+<Tabs groupId="lib" items={["Zod", "Zod Mini"]}>
+<Tab value="Zod">
+```ts
+const fileSchema = z.file();
+
+fileSchema.min(10_000);
+fileSchema.max(1_000_000);
+fileSchema.mime(["image/png"]);
+```
+</Tab>
+<Tab value='zod/v4-mini'>
+```ts
+const fileSchema = z.file().check(
+  z.minSize(10_000),
+  z.maxSize(1_000_000),
+  z.mime(["image/png"]),
+);
+```
+</Tab>
+</Tabs>
+
 ## Promises
 
 <Callout type="warn">
@@ -2515,4 +2540,3 @@ const MyFunction = z.function({
 
 const computeTrimmedLength = MyFunction.implement((input) => input.trim.length);
 ```
-
diff --git a/tmp/agent-patch-flux-pr-4567.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/docs/content/json-schema.mdx b/app/packages/docs/content/json-schema.mdx
index 7039b2d..9eed9c8 100644
--- a/tmp/agent-patch-flux-pr-4567.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/docs/content/json-schema.mdx
+++ b/app/packages/docs/content/json-schema.mdx
@@ -39,7 +39,6 @@ z.void(); // ❌
 z.date(); // ❌
 z.map(); // ❌
 z.set(); // ❌
-z.file(); // ❌
 z.transform(); // ❌
 z.nan(); // ❌
 z.custom(); // ❌
@@ -70,6 +69,26 @@ These schemas are supported via `contentEncoding`:
 z.base64(); // => { type: "string", contentEncoding: "base64" }
 ```
 
+## Files
+
+Zod represents `z.file()` as a binary string schema and preserves file-specific validation metadata on the emitted JSON Schema:
+
+```ts
+const schema = z.toJSONSchema(
+  z.file().min(10_000).max(1_000_000).mime(["image/png", "image/jpeg"])
+);
+
+// => {
+//   type: "string",
+//   format: "binary",
+//   minSize: 10_000,
+//   maxSize: 1_000_000,
+//   mime: ["image/png", "image/jpeg"]
+// }
+```
+
+When a file schema accepts exactly one MIME type, Zod also emits `contentMediaType` for compatibility with standard JSON Schema content metadata.
+
 All other string formats are supported via `pattern`:
 
 ```ts
@@ -312,7 +331,6 @@ z.void(); // ❌
 z.date(); // ❌
 z.map(); // ❌
 z.set(); // ❌
-z.file(); // ❌
 z.transform(); // ❌
 z.nan(); // ❌
 z.custom(); // ❌
diff --git a/tmp/agent-patch-flux-pr-4567.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/docs/content/v4/index.mdx b/app/packages/docs/content/v4/index.mdx
index 686841c..bd31814 100644
--- a/tmp/agent-patch-flux-pr-4567.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/docs/content/v4/index.mdx
+++ b/app/packages/docs/content/v4/index.mdx
@@ -584,7 +584,7 @@ const fileSchema = z.file();
 
 fileSchema.min(10_000); // minimum .size (bytes)
 fileSchema.max(1_000_000); // maximum .size (bytes)
-fileSchema.type("image/png"); // MIME type
+fileSchema.mime(["image/png"]); // MIME type
 ```
 
 ## Internationalization
diff --git a/tmp/agent-patch-flux-pr-4567.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/classic/tests/to-json-schema.test.ts b/app/packages/zod/src/v4/classic/tests/to-json-schema.test.ts
index 57efa33..85009b0 100644
--- a/tmp/agent-patch-flux-pr-4567.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/classic/tests/to-json-schema.test.ts
+++ b/app/packages/zod/src/v4/classic/tests/to-json-schema.test.ts
@@ -237,10 +237,6 @@ describe("toJSONSchema", () => {
     expect(() => z.toJSONSchema(z.set(z.string()))).toThrow("Set cannot be represented in JSON Schema");
     expect(() => z.toJSONSchema(z.custom(() => true))).toThrow("Custom types cannot be represented in JSON Schema");
 
-    // File type
-    const fileSchema = z.file();
-    expect(() => z.toJSONSchema(fileSchema)).toThrow("File cannot be represented in JSON Schema");
-
     // Transform
     const transformSchema = z.string().transform((val) => Number.parseInt(val));
     expect(() => z.toJSONSchema(transformSchema)).toThrow("Transforms cannot be represented in JSON Schema");
@@ -260,6 +256,43 @@ describe("toJSONSchema", () => {
     expect(() => z.toJSONSchema(dynamicCatchSchema)).toThrow("Dynamic catch values are not supported in JSON Schema");
   });
 
+  test("file schemas", () => {
+    expect(z.toJSONSchema(z.file())).toMatchInlineSnapshot(`
+      {
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
+        "format": "binary",
+        "type": "string",
+      }
+    `);
+
+    const fileSchema = z.file().min(10_000).max(1_000_000).mime(["image/png", "image/jpeg"]);
+    expect(z.toJSONSchema(fileSchema)).toMatchInlineSnapshot(`
+      {
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
+        "format": "binary",
+        "maxSize": 1000000,
+        "mime": [
+          "image/png",
+          "image/jpeg",
+        ],
+        "minSize": 10000,
+        "type": "string",
+      }
+    `);
+
+    expect(z.toJSONSchema(z.file().mime(["image/png"]))).toMatchInlineSnapshot(`
+      {
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
+        "contentMediaType": "image/png",
+        "format": "binary",
+        "mime": [
+          "image/png",
+        ],
+        "type": "string",
+      }
+    `);
+  });
+
   test("string formats", () => {
     expect(z.toJSONSchema(z.string().email())).toMatchInlineSnapshot(`
       {
diff --git a/tmp/agent-patch-flux-pr-4567.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/core/to-json-schema.ts b/app/packages/zod/src/v4/core/to-json-schema.ts
index ca1a680..7efa5da 100644
--- a/tmp/agent-patch-flux-pr-4567.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/core/to-json-schema.ts
+++ b/app/packages/zod/src/v4/core/to-json-schema.ts
@@ -434,8 +434,16 @@ export class JSONSchemaGenerator {
           break;
         }
         case "file": {
-          if (this.unrepresentable === "throw") {
-            throw new Error("File cannot be represented in JSON Schema");
+          const json: JSONSchema.BaseSchema = _json as any;
+          const { minimum, maximum, mime } = schema._zod.bag;
+
+          json.type = "string";
+          json.format = "binary";
+          if (typeof minimum === "number") json.minSize = minimum;
+          if (typeof maximum === "number") json.maxSize = maximum;
+          if (Array.isArray(mime) && mime.length > 0) {
+            json.mime = mime;
+            if (mime.length === 1) json.contentMediaType = mime[0];
           }
           break;
         }