[{"data":1,"prerenderedAt":831},["ShallowReactive",2],{"content-\u002Fdocs\u002Freading-the-form\u002Ferrors":3},{"id":4,"title":5,"body":6,"description":813,"extension":814,"meta":815,"metaRows":816,"navigation":146,"path":826,"seo":827,"source":828,"stem":829,"__hash__":830},"docs\u002Fdocs\u002Freading-the-form\u002Ferrors.md","errors",{"type":7,"value":8,"toc":804},"minimark",[9,15,22,25,43,47,52,206,245,252,321,346,350,355,398,423,431,438,541,562,600,618,629,635,639,642,693,724,728,800],[10,11,12],"h1",{"id":5},[13,14,5],"code",{},[16,17,18],"blockquote",{},[19,20,21],"p",{},"A reactive Proxy keyed by schema paths. Every leaf carries its current error list, container reads aggregate everything underneath, and the whole tree re-renders the moment validation re-runs.",[23,24],"docs-meta-table",{},[19,26,27,30,31,34,35,38,39,42],{},[13,28,29],{},"form.errors"," is the raw validation surface, paired one-to-one with ",[13,32,33],{},"form.values"," and ",[13,36,37],{},"form.fields",". The demo seeds three invalid values up front so the panels light up on mount, then updates live as you edit each field. The container panel shows the live ",[13,40,41],{},"profile"," sub-tree; the whole-form panel shows the full sparse tree.",[44,45],"docs-demo",{"label":46,"slug":5},"form.errors Demo",[48,49,51],"h2",{"id":50},"leaf-reads","Leaf reads",[53,54,59],"pre",{"className":55,"code":56,"language":57,"meta":58,"style":58},"language-ts shiki shiki-themes github-light github-dark","const schema = z.object({\n  email: z.email('Enter a valid email'),\n  name: z.string().min(1, 'Name is required'),\n})\n\nconst form = useForm({ schema })\n\nform.errors.email \u002F\u002F readonly ValidationError[]\nform.errors.email[0]?.message \u002F\u002F 'Enter a valid email' | undefined\nform.errors.email.length \u002F\u002F 0 when valid\n","ts","",[13,60,61,88,107,135,141,148,164,169,179,194],{"__ignoreMap":58},[62,63,66,70,74,77,81,85],"span",{"class":64,"line":65},"line",1,[62,67,69],{"class":68},"szBVR","const",[62,71,73],{"class":72},"sj4cs"," schema",[62,75,76],{"class":68}," =",[62,78,80],{"class":79},"sVt8B"," z.",[62,82,84],{"class":83},"sScJk","object",[62,86,87],{"class":79},"({\n",[62,89,91,94,97,100,104],{"class":64,"line":90},2,[62,92,93],{"class":79},"  email: z.",[62,95,96],{"class":83},"email",[62,98,99],{"class":79},"(",[62,101,103],{"class":102},"sZZnC","'Enter a valid email'",[62,105,106],{"class":79},"),\n",[62,108,110,113,116,119,122,124,127,130,133],{"class":64,"line":109},3,[62,111,112],{"class":79},"  name: z.",[62,114,115],{"class":83},"string",[62,117,118],{"class":79},"().",[62,120,121],{"class":83},"min",[62,123,99],{"class":79},[62,125,126],{"class":72},"1",[62,128,129],{"class":79},", ",[62,131,132],{"class":102},"'Name is required'",[62,134,106],{"class":79},[62,136,138],{"class":64,"line":137},4,[62,139,140],{"class":79},"})\n",[62,142,144],{"class":64,"line":143},5,[62,145,147],{"emptyLinePlaceholder":146},true,"\n",[62,149,151,153,156,158,161],{"class":64,"line":150},6,[62,152,69],{"class":68},[62,154,155],{"class":72}," form",[62,157,76],{"class":68},[62,159,160],{"class":83}," useForm",[62,162,163],{"class":79},"({ schema })\n",[62,165,167],{"class":64,"line":166},7,[62,168,147],{"emptyLinePlaceholder":146},[62,170,172,175],{"class":64,"line":171},8,[62,173,174],{"class":79},"form.errors.email ",[62,176,178],{"class":177},"sJ8bj","\u002F\u002F readonly ValidationError[]\n",[62,180,182,185,188,191],{"class":64,"line":181},9,[62,183,184],{"class":79},"form.errors.email[",[62,186,187],{"class":72},"0",[62,189,190],{"class":79},"]?.message ",[62,192,193],{"class":177},"\u002F\u002F 'Enter a valid email' | undefined\n",[62,195,197,200,203],{"class":64,"line":196},10,[62,198,199],{"class":79},"form.errors.email.",[62,201,202],{"class":72},"length",[62,204,205],{"class":177}," \u002F\u002F 0 when valid\n",[19,207,208,209,212,213,216,217,220,221,224,225,227,228,231,232,240,241,244],{},"A static object leaf always returns an array, ",[13,210,211],{},"readonly ValidationError[]",", empty when valid. Reads past a dynamic boundary carry ",[13,214,215],{},"| undefined",", because the node there may be absent: a numeric array index (",[13,218,219],{},"form.errors.todos[3]?.title","), a record key (",[13,222,223],{},"form.errors.byId.missing","), or a field that only exists on an inactive discriminated-union variant. That ",[13,226,215],{}," is honest. Dot and index access is pure navigation, so a key the schema doesn't declare and the data doesn't hold reads ",[13,229,230],{},"undefined",", never a stand-in proxy, and a truthy check agrees with the runtime. The one exception is a server error parked at a non-schema key: the error stores count as holding it, so it stays reachable and its message lands on the ",[233,234,236,239],"a",{"href":235},"#the-sentinel-container-self-errors",[13,237,238],{},"''"," container-self sentinel"," (",[13,242,243],{},"form.errors.ghost['']",").",[19,246,247,248,251],{},"The first error's ",[13,249,250],{},".message"," is what most templates render:",[53,253,257],{"className":254,"code":255,"language":256,"meta":58,"style":58},"language-vue shiki shiki-themes github-light github-dark","\u003Ctemplate>\n  \u003Cinput v-register=\"form.register('email')\" \u002F>\n  \u003Cp v-if=\"form.errors.email.length\">{{ form.errors.email[0]?.message }}\u003C\u002Fp>\n\u003C\u002Ftemplate>\n","vue",[13,258,259,271,291,312],{"__ignoreMap":58},[62,260,261,264,268],{"class":64,"line":65},[62,262,263],{"class":79},"\u003C",[62,265,267],{"class":266},"s9eBZ","template",[62,269,270],{"class":79},">\n",[62,272,273,276,279,282,285,288],{"class":64,"line":90},[62,274,275],{"class":79},"  \u003C",[62,277,278],{"class":266},"input",[62,280,281],{"class":83}," v-register",[62,283,284],{"class":79},"=",[62,286,287],{"class":102},"\"form.register('email')\"",[62,289,290],{"class":79}," \u002F>\n",[62,292,293,295,297,300,302,305,308,310],{"class":64,"line":109},[62,294,275],{"class":79},[62,296,19],{"class":266},[62,298,299],{"class":83}," v-if",[62,301,284],{"class":79},[62,303,304],{"class":102},"\"form.errors.email.length\"",[62,306,307],{"class":79},">{{ form.errors.email[0]?.message }}\u003C\u002F",[62,309,19],{"class":266},[62,311,270],{"class":79},[62,313,314,317,319],{"class":64,"line":137},[62,315,316],{"class":79},"\u003C\u002F",[62,318,267],{"class":266},[62,320,270],{"class":79},[19,322,323,324,330,331,337,338,341,342,345],{},"For display ergonomics, gating by ",[233,325,327],{"href":326},"\u002Fdocs\u002Fvalidation\u002Fshowing-errors",[13,328,329],{},"getDisplayState"," and pulling the first error in one shot, reach for ",[233,332,334],{"href":333},"\u002Fdocs\u002Freading-the-form\u002Ffields",[13,335,336],{},"form.fields.email.firstError"," paired with ",[13,339,340],{},"form.fields.email.showErrors",". The errors Proxy is the raw aggregate; the fields Proxy is the same data with display gating and ",[13,343,344],{},"firstError"," sugar layered on.",[48,347,349],{"id":348},"container-reads","Container reads",[19,351,352,354],{},[13,353,29],{}," is a drillable Proxy: dot-access descends into containers (returning a sub-Proxy you can keep drilling), and the call form returns a flat aggregate at any path. The two surfaces serve different jobs:",[53,356,358],{"className":55,"code":357,"language":57,"meta":58,"style":58},"form.errors.profile \u002F\u002F sub-Proxy: { '': [...refines], bio: [...], ... }\nform.errors('profile') \u002F\u002F flat array: every error inside profile + container-self\nform.errors() \u002F\u002F flat array: every error in the form\n",[13,359,360,368,386],{"__ignoreMap":58},[62,361,362,365],{"class":64,"line":65},[62,363,364],{"class":79},"form.errors.profile ",[62,366,367],{"class":177},"\u002F\u002F sub-Proxy: { '': [...refines], bio: [...], ... }\n",[62,369,370,373,375,377,380,383],{"class":64,"line":90},[62,371,372],{"class":79},"form.",[62,374,5],{"class":83},[62,376,99],{"class":79},[62,378,379],{"class":102},"'profile'",[62,381,382],{"class":79},") ",[62,384,385],{"class":177},"\u002F\u002F flat array: every error inside profile + container-self\n",[62,387,388,390,392,395],{"class":64,"line":109},[62,389,372],{"class":79},[62,391,5],{"class":83},[62,393,394],{"class":79},"() ",[62,396,397],{"class":177},"\u002F\u002F flat array: every error in the form\n",[19,399,400,403,404,407,408,414,415,418,419,422],{},[13,401,402],{},"form.errors()"," is the cheapest \"is anything wrong?\" check (",[13,405,406],{},"form.errors().length === 0"," when the form is valid). For aggregated counts and submission-state bits, see ",[233,409,411],{"href":410},"\u002Fdocs\u002Freading-the-form\u002Fmeta",[13,412,413],{},"form.meta",". When you serialize the dot-form (",[13,416,417],{},"JSON.stringify(form.errors)"," or ",[13,420,421],{},"{{ form.errors }}"," in a template), the Proxy materializes the live sparse tree, so you can dump the whole error state for debugging without losing structure.",[424,425,427,428,430],"h3",{"id":426},"the-sentinel-container-self-errors","The ",[13,429,238],{}," sentinel: container-self errors",[19,432,433,434,437],{},"A cross-field ",[13,435,436],{},".refine()"," lives on a container, not a leaf:",[53,439,441],{"className":55,"code":440,"language":57,"meta":58,"style":58},"const schema = z.object({\n  profile: z\n    .object({\n      bio: z.string().max(50),\n      handle: z.string(),\n    })\n    .refine((p) => p.bio.includes(p.handle), 'Bio must mention your handle'),\n})\n",[13,442,443,457,462,471,490,500,505,537],{"__ignoreMap":58},[62,444,445,447,449,451,453,455],{"class":64,"line":65},[62,446,69],{"class":68},[62,448,73],{"class":72},[62,450,76],{"class":68},[62,452,80],{"class":79},[62,454,84],{"class":83},[62,456,87],{"class":79},[62,458,459],{"class":64,"line":90},[62,460,461],{"class":79},"  profile: z\n",[62,463,464,467,469],{"class":64,"line":109},[62,465,466],{"class":79},"    .",[62,468,84],{"class":83},[62,470,87],{"class":79},[62,472,473,476,478,480,483,485,488],{"class":64,"line":137},[62,474,475],{"class":79},"      bio: z.",[62,477,115],{"class":83},[62,479,118],{"class":79},[62,481,482],{"class":83},"max",[62,484,99],{"class":79},[62,486,487],{"class":72},"50",[62,489,106],{"class":79},[62,491,492,495,497],{"class":64,"line":143},[62,493,494],{"class":79},"      handle: z.",[62,496,115],{"class":83},[62,498,499],{"class":79},"(),\n",[62,501,502],{"class":64,"line":150},[62,503,504],{"class":79},"    })\n",[62,506,507,509,512,515,518,520,523,526,529,532,535],{"class":64,"line":166},[62,508,466],{"class":79},[62,510,511],{"class":83},"refine",[62,513,514],{"class":79},"((",[62,516,19],{"class":517},"s4XuR",[62,519,382],{"class":79},[62,521,522],{"class":68},"=>",[62,524,525],{"class":79}," p.bio.",[62,527,528],{"class":83},"includes",[62,530,531],{"class":79},"(p.handle), ",[62,533,534],{"class":102},"'Bio must mention your handle'",[62,536,106],{"class":79},[62,538,539],{"class":64,"line":171},[62,540,140],{"class":79},[19,542,543,544,547,548,551,552,34,555,558,559,561],{},"The refine's error path is ",[13,545,546],{},"['profile']",", the container itself. To keep ",[13,549,550],{},"form.errors.profile"," readable alongside leaf errors at ",[13,553,554],{},"['profile', 'bio']",[13,556,557],{},"['profile', 'handle']",", container-self errors land in the materialized tree under the ",[13,560,238],{}," sentinel slot:",[53,563,565],{"className":55,"code":564,"language":57,"meta":58,"style":58},"form.errors.profile[''] \u002F\u002F refine errors on profile (and any other container-self entries)\nform.errors.profile.bio \u002F\u002F leaf errors on bio\nform.errors[''] \u002F\u002F root form-level errors (setFormErrors, root refines)\n",[13,566,567,580,588],{"__ignoreMap":58},[62,568,569,572,574,577],{"class":64,"line":65},[62,570,571],{"class":79},"form.errors.profile[",[62,573,238],{"class":102},[62,575,576],{"class":79},"] ",[62,578,579],{"class":177},"\u002F\u002F refine errors on profile (and any other container-self entries)\n",[62,581,582,585],{"class":64,"line":90},[62,583,584],{"class":79},"form.errors.profile.bio ",[62,586,587],{"class":177},"\u002F\u002F leaf errors on bio\n",[62,589,590,593,595,597],{"class":64,"line":109},[62,591,592],{"class":79},"form.errors[",[62,594,238],{"class":102},[62,596,576],{"class":79},[62,598,599],{"class":177},"\u002F\u002F root form-level errors (setFormErrors, root refines)\n",[19,601,602,605,606,609,610,613,614,617],{},[13,603,604],{},"JSON.stringify(form.errors.profile)"," materializes as ",[13,607,608],{},"{ '': [refineError], bio: [maxError], handle: [...] }",". Both the refine and the descendant leaves coexist; nothing clobbers anything. The same convention reaches all the way down: a refine on ",[13,611,612],{},"profile.address"," lands at ",[13,615,616],{},"form.errors.profile.address['']",".",[19,619,620,621,624,625,628],{},"The call form is the flat alternative: ",[13,622,623],{},"form.errors('profile')"," returns one ",[13,626,627],{},"ValidationError[]"," containing the refine PLUS every descendant leaf error in declaration order, no structure. Reach for the structural tree when you want to render per-field; reach for the call form when you want \"anything wrong under this container?\".",[19,630,631,632,634],{},"If your schema legitimately declares a field literally named ",[13,633,238],{}," (an exceptionally rare choice), the literal leaf's own errors and any container-self errors share the slot. Both arrays concatenate into a single read.",[48,636,638],{"id":637},"setting-errors-imperatively","Setting errors imperatively",[19,640,641],{},"Server-side errors land in the same reactive store as Zod errors:",[53,643,645],{"className":55,"code":644,"language":57,"meta":58,"style":58},"form.setFieldErrors([\n  { path: 'email', message: 'Already taken' },\n  { path: 'profile.handle', message: 'Reserved' },\n])\n",[13,646,647,657,674,688],{"__ignoreMap":58},[62,648,649,651,654],{"class":64,"line":65},[62,650,372],{"class":79},[62,652,653],{"class":83},"setFieldErrors",[62,655,656],{"class":79},"([\n",[62,658,659,662,665,668,671],{"class":64,"line":90},[62,660,661],{"class":79},"  { path: ",[62,663,664],{"class":102},"'email'",[62,666,667],{"class":79},", message: ",[62,669,670],{"class":102},"'Already taken'",[62,672,673],{"class":79}," },\n",[62,675,676,678,681,683,686],{"class":64,"line":109},[62,677,661],{"class":79},[62,679,680],{"class":102},"'profile.handle'",[62,682,667],{"class":79},[62,684,685],{"class":102},"'Reserved'",[62,687,673],{"class":79},[62,689,690],{"class":64,"line":137},[62,691,692],{"class":79},"])\n",[19,694,695,34,698,701,702,705,706,709,710,716,717,720,721,723],{},[13,696,697],{},"form.errors.email",[13,699,700],{},"form.errors.profile.handle"," update immediately, and any ",[13,703,704],{},"form.fields.\u003Cpath>.firstError"," \u002F ",[13,707,708],{},"form.fields.\u003Cpath>.showErrors"," reads update with them. Pair with ",[233,711,713],{"href":712},"\u002Fdocs\u002Fserver-and-ssr\u002Fparse-api-errors",[13,714,715],{},"parseApiErrors"," to convert a server response payload into the ",[13,718,719],{},"{ path, message }"," shape ",[13,722,653],{}," expects; the render surface is identical whether the error came from Zod or your API.",[48,725,727],{"id":726},"where-to-next","Where to next",[729,730,731,739,748,762,780,787],"ul",{},[732,733,734,738],"li",{},[233,735,737],{"href":736},"\u002Fdocs\u002Freading-the-form\u002Fthe-form","The form",": every other reactive read.",[732,740,741,747],{},[233,742,744],{"href":743},"\u002Fdocs\u002Freading-the-form\u002Fvalues",[13,745,746],{},"values",": the read companion to errors.",[732,749,750,755,756,705,758,761],{},[233,751,752],{"href":333},[13,753,754],{},"fields",": per-leaf state, including the gated ",[13,757,344],{},[13,759,760],{},"showErrors"," pairing.",[732,763,764,769,770,129,773,129,776,779],{},[233,765,766],{"href":410},[13,767,768],{},"meta",": the form-level aggregates (",[13,771,772],{},"errorCount",[13,774,775],{},"valid",[13,777,778],{},"submitting",", etc.).",[732,781,782,786],{},[233,783,785],{"href":784},"\u002Fdocs\u002Fvalidation\u002Fwhen-validation-runs","When validation runs",": the moment errors appear.",[732,788,789,793,794,796,797,799],{},[233,790,792],{"href":791},"\u002Fdocs\u002Fsubmitting\u002Fserver-side-errors","Server-side errors",": ",[13,795,653],{}," + ",[13,798,715],{}," in full.",[801,802,803],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":58,"searchDepth":90,"depth":90,"links":805},[806,807,811,812],{"id":50,"depth":90,"text":51},{"id":348,"depth":90,"text":349,"children":808},[809],{"id":426,"depth":109,"text":810},"The '' sentinel: container-self errors",{"id":637,"depth":90,"text":638},{"id":726,"depth":90,"text":727},"form.errors is a reactive Proxy keyed by schema paths. Read any leaf's error list, with errors[0]?.message ready to render and the full array available for richer surfaces.","md",{},[817,820,823],{"label":818,"value":819},"Category","Return property",{"label":821,"value":822,"kind":13},"Type","ErrorsProxyShape\u003CForm>",{"label":824,"value":825},"Reactive","Yes","\u002Fdocs\u002Freading-the-form\u002Ferrors",{"title":5,"description":813},null,"docs\u002Freading-the-form\u002Ferrors","HqVsXCx84NaTQDcgTvATrPFm0HHj3JVNRAp0G-MQT-0",1780949758539]