AF15
The schema root is not a shape that can hold keys, so there is nothing for Attaform to address.
In development this reads:
attaform/zod useForm schema root must be a ZodObject, ZodRecord, or ZodDiscriminatedUnion (got 'string'). Wrap other shapes under a key.
The Zod v3 adapter prints the mirror of that, prefixed [attaform/zod-v3] and naming the root by its Zod type ('ZodString') rather than by its kind. Either way, the production message carries that same name after the code.
What happened
A form is a set of addressable fields. Every part of Attaform that you reach for names a path into that set: register('email'), form.errors('address.city'), form.values.total. A root has to be able to hold keys for any of those to mean anything, and three shapes can:
| Root | What it models |
|---|---|
z.object({ ... }) | A fixed set of fields, the common case |
z.record(key, value) | A dictionary whose keys are known at runtime |
z.discriminatedUnion(key, [ ... ]) | A variant form; the active branch lifts its keys into one surface |
A z.string(), a z.array(...), a bare z.union(...), a z.map(...): none of these has a key to bind an input to.
This is the only shape of schema Attaform refuses, and it refuses on the absence of the one property the form engine needs, not on a list of kinds. Every Zod kind is welcome under a key, including the ones with no obvious form representation. Attaform does not decide what a field is allowed to hold.
How to fix it
Give the value a name:
// Refused: nothing to address.
const schema = z.string()
// Accepted.
const schema = z.object({ nickname: z.string() })
The same move works for every other refused root:
const schema = z.object({
tags: z.array(z.string()),
lookup: z.map(z.string(), z.number()),
payload: z.union([z.string(), z.number()]),
})
If the form genuinely has runtime-known keys rather than a fixed shape, reach for a record root instead of wrapping:
const schema = z.record(z.string(), z.number())
The schema contract covers what Attaform asks of a schema and how the adapters read it.