[{"data":1,"prerenderedAt":1202},["ShallowReactive",2],{"content-\u002Fdocs\u002Fserver-and-ssr\u002Fparse-api-errors":3},{"id":4,"title":5,"body":6,"description":1180,"extension":1181,"meta":1182,"metaRows":1183,"navigation":1196,"path":1197,"seo":1198,"source":1199,"stem":1200,"__hash__":1201},"docs\u002Fdocs\u002Fserver-and-ssr\u002Fparse-api-errors.md","Parsing API errors",{"type":7,"value":8,"toc":1165},"minimark",[9,13,25,28,50,55,94,127,131,134,204,228,300,304,309,426,437,441,514,524,528,531,640,646,650,653,717,728,739,743,759,885,895,899,902,952,1021,1027,1031,1034,1037,1081,1085,1088,1125,1132,1136,1161],[10,11,5],"h1",{"id":12},"parsing-api-errors",[14,15,16],"blockquote",{},[17,18,19,20,24],"p",{},"Map server failure responses into Attaform's reactive error store via ",[21,22,23],"code",{},"parseApiErrors",". JSON envelopes or bare details records, dotted paths, codes preserved end-to-end.",[26,27],"docs-meta-table",{},[17,29,30,31,35,36,39,40,45,46,49],{},"This page focuses on the ",[32,33,34],"strong",{},"parser surface",": the envelope shapes the helper accepts, the discriminated result type, and how ",[21,37,38],{},"setFieldErrors"," mounts the parsed entries. The ",[41,42,44],"a",{"href":43},"\u002Fdocs\u002Fsubmitting\u002Fserver-side-errors","Server-side errors"," page covers the full pipeline integration with ",[21,47,48],{},"handleSubmit",". Both pages reference the same parser; this one's the deep dive.",[51,52,54],"h2",{"id":53},"the-signature","The signature",[56,57,62],"pre",{"className":58,"code":59,"language":60,"meta":61,"style":61},"language-ts shiki shiki-themes github-light github-dark","parseApiErrors(\n  envelope: unknown,\n  options: { formKey: string }\n): ParseApiErrorsResult\n","ts","",[21,63,64,76,82,88],{"__ignoreMap":61},[65,66,69,72],"span",{"class":67,"line":68},"line",1,[65,70,23],{"class":71},"sScJk",[65,73,75],{"class":74},"sVt8B","(\n",[65,77,79],{"class":67,"line":78},2,[65,80,81],{"class":74},"  envelope: unknown,\n",[65,83,85],{"class":67,"line":84},3,[65,86,87],{"class":74},"  options: { formKey: string }\n",[65,89,91],{"class":67,"line":90},4,[65,92,93],{"class":74},"): ParseApiErrorsResult\n",[17,95,96,99,100,103,104,107,108,111,112,115,116,119,120,119,123,126],{},[21,97,98],{},"envelope"," is whatever the server returned; the parser introspects it to figure out which shape it received. ",[21,101,102],{},"options.formKey"," stamps each produced ",[21,105,106],{},"ValidationError"," with the form's key for cross-form isolation. The full options bag also accepts ",[21,109,110],{},"defaultCode"," (default ",[21,113,114],{},"'api:unknown'",", used for bare-string entries) and the size caps ",[21,117,118],{},"maxEntries"," \u002F ",[21,121,122],{},"maxPathDepth",[21,124,125],{},"maxTotalSegments"," for hostile-payload protection.",[51,128,130],{"id":129},"the-result-type","The result type",[17,132,133],{},"A discriminated result so malformed payloads don't throw:",[56,135,137],{"className":58,"code":136,"language":60,"meta":61,"style":61},"type ParseApiErrorsResult = {\n  readonly ok: boolean\n  readonly errors: ValidationError[]\n  readonly rejected?: string\n}\n",[21,138,139,154,170,185,198],{"__ignoreMap":61},[65,140,141,145,148,151],{"class":67,"line":68},[65,142,144],{"class":143},"szBVR","type",[65,146,147],{"class":71}," ParseApiErrorsResult",[65,149,150],{"class":143}," =",[65,152,153],{"class":74}," {\n",[65,155,156,159,163,166],{"class":67,"line":78},[65,157,158],{"class":143},"  readonly",[65,160,162],{"class":161},"s4XuR"," ok",[65,164,165],{"class":143},":",[65,167,169],{"class":168},"sj4cs"," boolean\n",[65,171,172,174,177,179,182],{"class":67,"line":84},[65,173,158],{"class":143},[65,175,176],{"class":161}," errors",[65,178,165],{"class":143},[65,180,181],{"class":71}," ValidationError",[65,183,184],{"class":74},"[]\n",[65,186,187,189,192,195],{"class":67,"line":90},[65,188,158],{"class":143},[65,190,191],{"class":161}," rejected",[65,193,194],{"class":143},"?:",[65,196,197],{"class":168}," string\n",[65,199,201],{"class":67,"line":200},5,[65,202,203],{"class":74},"}\n",[205,206,207,218],"ul",{},[208,209,210,213,214,217],"li",{},[21,211,212],{},"{ ok: true, errors }",": envelope recognized. ",[21,215,216],{},"errors"," may be empty (server returned a 422 with no field-level details).",[208,219,220,223,224,227],{},[21,221,222],{},"{ ok: false, errors: [], rejected }",": envelope shape wasn't recognized. ",[21,225,226],{},"rejected"," carries a one-line description (\"payload was string, expected object\", etc.). Log it; don't apply.",[56,229,231],{"className":58,"code":230,"language":60,"meta":61,"style":61},"const result = parseApiErrors(err.data, { formKey: form.key })\nif (result.ok) {\n  form.setFieldErrors(result.errors)\n} else {\n  console.error('Unexpected error payload:', result.rejected, err.data)\n}\n",[21,232,233,249,257,267,277,295],{"__ignoreMap":61},[65,234,235,238,241,243,246],{"class":67,"line":68},[65,236,237],{"class":143},"const",[65,239,240],{"class":168}," result",[65,242,150],{"class":143},[65,244,245],{"class":71}," parseApiErrors",[65,247,248],{"class":74},"(err.data, { formKey: form.key })\n",[65,250,251,254],{"class":67,"line":78},[65,252,253],{"class":143},"if",[65,255,256],{"class":74}," (result.ok) {\n",[65,258,259,262,264],{"class":67,"line":84},[65,260,261],{"class":74},"  form.",[65,263,38],{"class":71},[65,265,266],{"class":74},"(result.errors)\n",[65,268,269,272,275],{"class":67,"line":90},[65,270,271],{"class":74},"} ",[65,273,274],{"class":143},"else",[65,276,153],{"class":74},[65,278,279,282,285,288,292],{"class":67,"line":200},[65,280,281],{"class":74},"  console.",[65,283,284],{"class":71},"error",[65,286,287],{"class":74},"(",[65,289,291],{"class":290},"sZZnC","'Unexpected error payload:'",[65,293,294],{"class":74},", result.rejected, err.data)\n",[65,296,298],{"class":67,"line":297},6,[65,299,203],{"class":74},[51,301,303],{"id":302},"envelope-shapes","Envelope shapes",[305,306,308],"h3",{"id":307},"wrapped-envelope","Wrapped envelope",[56,310,312],{"className":58,"code":311,"language":60,"meta":61,"style":61},"{\n  error: {\n    details: {\n      email: { message: 'already taken', code: 'api:duplicate-email' },\n      password: [\n        { message: 'too short', code: 'api:min-length' },\n        { message: 'must contain a digit', code: 'api:digit-required' },\n      ],\n    },\n  },\n}\n",[21,313,314,319,327,334,364,372,388,403,409,415,421],{"__ignoreMap":61},[65,315,316],{"class":67,"line":68},[65,317,318],{"class":74},"{\n",[65,320,321,324],{"class":67,"line":78},[65,322,323],{"class":71},"  error",[65,325,326],{"class":74},": {\n",[65,328,329,332],{"class":67,"line":84},[65,330,331],{"class":71},"    details",[65,333,326],{"class":74},[65,335,336,339,342,345,348,351,354,356,358,361],{"class":67,"line":90},[65,337,338],{"class":71},"      email",[65,340,341],{"class":74},": { ",[65,343,344],{"class":71},"message",[65,346,347],{"class":74},": ",[65,349,350],{"class":290},"'already taken'",[65,352,353],{"class":74},", ",[65,355,21],{"class":71},[65,357,347],{"class":74},[65,359,360],{"class":290},"'api:duplicate-email'",[65,362,363],{"class":74}," },\n",[65,365,366,369],{"class":67,"line":200},[65,367,368],{"class":71},"      password",[65,370,371],{"class":74},": [\n",[65,373,374,377,380,383,386],{"class":67,"line":297},[65,375,376],{"class":74},"        { message: ",[65,378,379],{"class":290},"'too short'",[65,381,382],{"class":74},", code: ",[65,384,385],{"class":290},"'api:min-length'",[65,387,363],{"class":74},[65,389,391,393,396,398,401],{"class":67,"line":390},7,[65,392,376],{"class":74},[65,394,395],{"class":290},"'must contain a digit'",[65,397,382],{"class":74},[65,399,400],{"class":290},"'api:digit-required'",[65,402,363],{"class":74},[65,404,406],{"class":67,"line":405},8,[65,407,408],{"class":74},"      ],\n",[65,410,412],{"class":67,"line":411},9,[65,413,414],{"class":74},"    },\n",[65,416,418],{"class":67,"line":417},10,[65,419,420],{"class":74},"  },\n",[65,422,424],{"class":67,"line":423},11,[65,425,203],{"class":74},[17,427,428,429,432,433,436],{},"The parser walks to ",[21,430,431],{},"error.details"," and reads each key-value pair as ",[21,434,435],{},"path → ValidationError[]",".",[305,438,440],{"id":439},"bare-details-record","Bare details record",[56,442,444],{"className":58,"code":443,"language":60,"meta":61,"style":61},"{\n  email: { message: 'already taken', code: 'api:duplicate-email' },\n  password: [\n    { message: 'too short', code: 'api:min-length' },\n    { message: 'must contain a digit', code: 'api:digit-required' },\n  ],\n}\n",[21,445,446,450,473,480,493,505,510],{"__ignoreMap":61},[65,447,448],{"class":67,"line":68},[65,449,318],{"class":74},[65,451,452,455,457,459,461,463,465,467,469,471],{"class":67,"line":78},[65,453,454],{"class":71},"  email",[65,456,341],{"class":74},[65,458,344],{"class":71},[65,460,347],{"class":74},[65,462,350],{"class":290},[65,464,353],{"class":74},[65,466,21],{"class":71},[65,468,347],{"class":74},[65,470,360],{"class":290},[65,472,363],{"class":74},[65,474,475,478],{"class":67,"line":84},[65,476,477],{"class":71},"  password",[65,479,371],{"class":74},[65,481,482,485,487,489,491],{"class":67,"line":90},[65,483,484],{"class":74},"    { message: ",[65,486,379],{"class":290},[65,488,382],{"class":74},[65,490,385],{"class":290},[65,492,363],{"class":74},[65,494,495,497,499,501,503],{"class":67,"line":200},[65,496,484],{"class":74},[65,498,395],{"class":290},[65,500,382],{"class":74},[65,502,400],{"class":290},[65,504,363],{"class":74},[65,506,507],{"class":67,"line":297},[65,508,509],{"class":74},"  ],\n",[65,511,512],{"class":67,"line":390},[65,513,203],{"class":74},[17,515,516,517,519,520,523],{},"Same logic, no ",[21,518,431],{}," wrapper; the parser introspects the top level for ",[21,521,522],{},"{ message, code }"," entries (or arrays thereof) keyed by path. Pick this shape when the API already returns flat field-error records.",[305,525,527],{"id":526},"entry-shape","Entry shape",[17,529,530],{},"Each detail value is one of two shapes (or a mix-and-match array of both):",[532,533,534,575],"ol",{},[208,535,536,347,539,542,543,545,546,436,548],{},[32,537,538],{},"Structured",[21,540,541],{},"{ message: string, code: string }",". The ",[21,544,21],{}," is forwarded verbatim onto the produced ",[21,547,106],{},[56,549,551],{"className":58,"code":550,"language":60,"meta":61,"style":61},"{ message: 'already taken', code: 'api:duplicate-email' }\n",[21,552,553],{"__ignoreMap":61},[65,554,555,558,560,562,564,566,568,570,572],{"class":67,"line":68},[65,556,557],{"class":74},"{ ",[65,559,344],{"class":71},[65,561,347],{"class":74},[65,563,350],{"class":290},[65,565,353],{"class":74},[65,567,21],{"class":71},[65,569,347],{"class":74},[65,571,360],{"class":290},[65,573,574],{"class":74}," }\n",[208,576,577,580,581,583,584,586,587,589,590,111,593,595,596,626,629,630,632,633,353,636,639],{},[32,578,579],{},"Bare-string",": a plain string. The parser synthesizes a ",[21,582,106],{}," with ",[21,585,344],{}," set to the string and ",[21,588,21],{}," set to ",[21,591,592],{},"options.defaultCode",[21,594,114],{},"). This is the Rails \u002F Django REST Framework \u002F FastAPI \u002F Laravel shape:",[56,597,599],{"className":58,"code":598,"language":60,"meta":61,"style":61},"{ email: ['Email already taken.'], username: 'too short' }\n",[21,600,601],{"__ignoreMap":61},[65,602,603,605,608,611,614,617,620,622,624],{"class":67,"line":68},[65,604,557],{"class":74},[65,606,607],{"class":71},"email",[65,609,610],{"class":74},": [",[65,612,613],{"class":290},"'Email already taken.'",[65,615,616],{"class":74},"], ",[65,618,619],{"class":71},"username",[65,621,347],{"class":74},[65,623,379],{"class":290},[65,625,574],{"class":74},[627,628],"br",{},"Pass a more specific ",[21,631,110],{}," (",[21,634,635],{},"'api:server-validation'",[21,637,638],{},"'myapp:legacy'",", etc.) when you know the source.",[17,641,642,643,645],{},"A field's value is either a single entry, an array, or a mix of structured and bare-string entries. Array entries each produce their own ",[21,644,106],{},", so a single field can carry multiple distinct failures.",[51,647,649],{"id":648},"path-shapes","Path shapes",[17,651,652],{},"Keys are dotted paths; the parser canonicalizes them into the form's path tuple:",[56,654,656],{"className":58,"code":655,"language":60,"meta":61,"style":61},"{\n  'user.email': { message: 'already taken', code: 'api:dup' },\n  'items.0.qty': { message: 'must be positive', code: 'api:pos' },\n  'meta.tags.2': { message: 'invalid tag', code: 'api:invalid-tag' },\n}\n",[21,657,658,662,679,696,713],{"__ignoreMap":61},[65,659,660],{"class":67,"line":68},[65,661,318],{"class":74},[65,663,664,667,670,672,674,677],{"class":67,"line":78},[65,665,666],{"class":290},"  'user.email'",[65,668,669],{"class":74},": { message: ",[65,671,350],{"class":290},[65,673,382],{"class":74},[65,675,676],{"class":290},"'api:dup'",[65,678,363],{"class":74},[65,680,681,684,686,689,691,694],{"class":67,"line":84},[65,682,683],{"class":290},"  'items.0.qty'",[65,685,669],{"class":74},[65,687,688],{"class":290},"'must be positive'",[65,690,382],{"class":74},[65,692,693],{"class":290},"'api:pos'",[65,695,363],{"class":74},[65,697,698,701,703,706,708,711],{"class":67,"line":90},[65,699,700],{"class":290},"  'meta.tags.2'",[65,702,669],{"class":74},[65,704,705],{"class":290},"'invalid tag'",[65,707,382],{"class":74},[65,709,710],{"class":290},"'api:invalid-tag'",[65,712,363],{"class":74},[65,714,715],{"class":67,"line":200},[65,716,203],{"class":74},[205,718,719,722,725],{},[208,720,721],{},"Plain field names → object property segments.",[208,723,724],{},"Numeric segments → array indices.",[208,726,727],{},"Multi-segment dots → nested object traversal.",[17,729,730,731,734,735,738],{},"Bracket notation is NOT recognized; ",[21,732,733],{},"items[0].qty"," doesn't parse. Use ",[21,736,737],{},"items.0.qty"," instead and match your API's serialization to the dotted-segment convention.",[51,740,742],{"id":741},"code-prefixes","Code prefixes",[17,744,745,746,353,749,353,752,755,756,758],{},"Pick a prefix for the codes (",[21,747,748],{},"api:",[21,750,751],{},"auth:",[21,753,754],{},"myapp:",") and stay consistent. The prefix lets renderers branch on ",[21,757,21],{}," rather than matching message strings:",[56,760,764],{"className":761,"code":762,"language":763,"meta":61,"style":61},"language-vue shiki shiki-themes github-light github-dark","\u003Ctemplate>\n  \u003Cspan v-if=\"form.errors.email?.[0]?.code === 'api:duplicate-email'\" class=\"error\">\n    That email is already registered.\n    \u003CNuxtLink :to=\"`\u002Fsign-in?email=${form.values.email}`\">Sign in instead?\u003C\u002FNuxtLink>\n  \u003C\u002Fspan>\n  \u003Cspan v-else-if=\"form.errors.email?.[0]\" class=\"error\">\n    {{ form.errors.email[0].message }}\n  \u003C\u002Fspan>\n\u003C\u002Ftemplate>\n","vue",[21,765,766,778,804,809,832,841,863,868,876],{"__ignoreMap":61},[65,767,768,771,775],{"class":67,"line":68},[65,769,770],{"class":74},"\u003C",[65,772,774],{"class":773},"s9eBZ","template",[65,776,777],{"class":74},">\n",[65,779,780,783,785,788,791,794,797,799,802],{"class":67,"line":78},[65,781,782],{"class":74},"  \u003C",[65,784,65],{"class":773},[65,786,787],{"class":71}," v-if",[65,789,790],{"class":74},"=",[65,792,793],{"class":290},"\"form.errors.email?.[0]?.code === 'api:duplicate-email'\"",[65,795,796],{"class":71}," class",[65,798,790],{"class":74},[65,800,801],{"class":290},"\"error\"",[65,803,777],{"class":74},[65,805,806],{"class":67,"line":84},[65,807,808],{"class":74},"    That email is already registered.\n",[65,810,811,814,817,820,822,825,828,830],{"class":67,"line":90},[65,812,813],{"class":74},"    \u003C",[65,815,816],{"class":773},"NuxtLink",[65,818,819],{"class":71}," :to",[65,821,790],{"class":74},[65,823,824],{"class":290},"\"`\u002Fsign-in?email=${form.values.email}`\"",[65,826,827],{"class":74},">Sign in instead?\u003C\u002F",[65,829,816],{"class":773},[65,831,777],{"class":74},[65,833,834,837,839],{"class":67,"line":200},[65,835,836],{"class":74},"  \u003C\u002F",[65,838,65],{"class":773},[65,840,777],{"class":74},[65,842,843,845,847,850,852,855,857,859,861],{"class":67,"line":297},[65,844,782],{"class":74},[65,846,65],{"class":773},[65,848,849],{"class":71}," v-else-if",[65,851,790],{"class":74},[65,853,854],{"class":290},"\"form.errors.email?.[0]\"",[65,856,796],{"class":71},[65,858,790],{"class":74},[65,860,801],{"class":290},[65,862,777],{"class":74},[65,864,865],{"class":67,"line":390},[65,866,867],{"class":74},"    {{ form.errors.email[0].message }}\n",[65,869,870,872,874],{"class":67,"line":405},[65,871,836],{"class":74},[65,873,65],{"class":773},[65,875,777],{"class":74},[65,877,878,881,883],{"class":67,"line":411},[65,879,880],{"class":74},"\u003C\u002F",[65,882,774],{"class":773},[65,884,777],{"class":74},[17,886,887,888,119,891,894],{},"The prefix also helps when a single field carries both schema and API errors: schema codes typically start ",[21,889,890],{},"zod:",[21,892,893],{},"atta:","; API codes start with your prefix. Filtering by prefix tells you which source emitted each entry.",[51,896,898],{"id":897},"mounting-parsed-errors","Mounting parsed errors",[17,900,901],{},"Three methods land parsed errors into the form:",[903,904,905,918],"table",{},[906,907,908],"thead",{},[909,910,911,915],"tr",{},[912,913,914],"th",{},"Method",[912,916,917],{},"Behavior",[919,920,921,932,942],"tbody",{},[909,922,923,929],{},[924,925,926],"td",{},[21,927,928],{},"setFieldErrors(errors)",[924,930,931],{},"Replace the user-error set with the supplied list. Common case after a fresh server round-trip.",[909,933,934,939],{},[924,935,936],{},[21,937,938],{},"addFieldErrors(errors)",[924,940,941],{},"Append to the existing set without clearing prior entries.",[909,943,944,949],{},[924,945,946],{},[21,947,948],{},"clearFieldErrors(path?)",[924,950,951],{},"Drop all errors (no arg) or just one path's user-errors.",[56,953,955],{"className":58,"code":954,"language":60,"meta":61,"style":61},"form.setFieldErrors(parsed.errors)\nform.addFieldErrors([{ path: ['email'], message: 'Already registered', code: 'custom' }])\nform.clearFieldErrors('email')\nform.clearFieldErrors() \u002F\u002F clear everything\n",[21,956,957,967,994,1008],{"__ignoreMap":61},[65,958,959,962,964],{"class":67,"line":68},[65,960,961],{"class":74},"form.",[65,963,38],{"class":71},[65,965,966],{"class":74},"(parsed.errors)\n",[65,968,969,971,974,977,980,983,986,988,991],{"class":67,"line":78},[65,970,961],{"class":74},[65,972,973],{"class":71},"addFieldErrors",[65,975,976],{"class":74},"([{ path: [",[65,978,979],{"class":290},"'email'",[65,981,982],{"class":74},"], message: ",[65,984,985],{"class":290},"'Already registered'",[65,987,382],{"class":74},[65,989,990],{"class":290},"'custom'",[65,992,993],{"class":74}," }])\n",[65,995,996,998,1001,1003,1005],{"class":67,"line":84},[65,997,961],{"class":74},[65,999,1000],{"class":71},"clearFieldErrors",[65,1002,287],{"class":74},[65,1004,979],{"class":290},[65,1006,1007],{"class":74},")\n",[65,1009,1010,1012,1014,1017],{"class":67,"line":90},[65,1011,961],{"class":74},[65,1013,1000],{"class":71},[65,1015,1016],{"class":74},"() ",[65,1018,1020],{"class":1019},"sJ8bj","\u002F\u002F clear everything\n",[17,1022,1023,1024,1026],{},"User-injected errors persist across schema revalidation and successful submits; they're stored separately from schema errors. Schema validation can't replace them, and a successful submit doesn't auto-clear them. The user's next keystroke re-runs schema validation against the field (updating the schema half), but your API entries stay until you call ",[21,1025,1000],{}," or unmount.",[51,1028,1030],{"id":1029},"auto-clear-on-edit","Auto-clear on edit?",[17,1032,1033],{},"By default, editing a field after a server error landed at that path does NOT auto-clear the error. Most servers want a fresh round-trip before the error is \"cleared,\" so this matches the network round-trip semantics.",[17,1035,1036],{},"For \"clear on edit\" UX, hook a watcher:",[56,1038,1040],{"className":58,"code":1039,"language":60,"meta":61,"style":61},"watch(\n  () => form.values.email,\n  () => form.clearFieldErrors('email')\n)\n",[21,1041,1042,1049,1060,1077],{"__ignoreMap":61},[65,1043,1044,1047],{"class":67,"line":68},[65,1045,1046],{"class":71},"watch",[65,1048,75],{"class":74},[65,1050,1051,1054,1057],{"class":67,"line":78},[65,1052,1053],{"class":74},"  () ",[65,1055,1056],{"class":143},"=>",[65,1058,1059],{"class":74}," form.values.email,\n",[65,1061,1062,1064,1066,1069,1071,1073,1075],{"class":67,"line":84},[65,1063,1053],{"class":74},[65,1065,1056],{"class":143},[65,1067,1068],{"class":74}," form.",[65,1070,1000],{"class":71},[65,1072,287],{"class":74},[65,1074,979],{"class":290},[65,1076,1007],{"class":74},[65,1078,1079],{"class":67,"line":90},[65,1080,1007],{"class":74},[51,1082,1084],{"id":1083},"reading-the-parsed-errors-in-the-template","Reading the parsed errors in the template",[17,1086,1087],{},"Once mounted, server errors are indistinguishable from schema errors at the read surfaces:",[205,1089,1090,1100,1106,1119],{},[208,1091,1092,1095,1096,1099],{},[21,1093,1094],{},"form.errors.email"," returns the ",[21,1097,1098],{},"ValidationError[]"," (server or schema, same shape).",[208,1101,1102,1105],{},[21,1103,1104],{},"form.fields.email.firstError"," returns the first one.",[208,1107,1108,1111,1112,1118],{},[21,1109,1110],{},"form.fields.email.showErrors"," gates display per the ",[41,1113,1115],{"href":1114},"\u002Fdocs\u002Fvalidation\u002Fshowing-errors",[21,1116,1117],{},"getDisplayState"," predicate.",[208,1120,1121,1124],{},[21,1122,1123],{},"focusFirstError()"," pulls focus to the first server error just like a schema one.",[17,1126,1127,1128,1131],{},"No special \"this is a server error\" surface in the template. The render code reads ",[21,1129,1130],{},"form.fields.\u003Cpath>.firstError?.message"," the same way for both kinds.",[51,1133,1135],{"id":1134},"where-to-next","Where to next",[205,1137,1138,1146,1154],{},[208,1139,1140,1142,1143,1145],{},[41,1141,44],{"href":43},": the full ",[21,1144,48],{}," integration story.",[208,1147,1148,1153],{},[41,1149,1151],{"href":1150},"\u002Fdocs\u002Fsubmitting\u002Fhandle-submit",[21,1152,48],{},": the dispatch surface that owns the success \u002F error split.",[208,1155,1156,1160],{},[41,1157,1159],{"href":1158},"\u002Fdocs\u002Fsubmitting\u002Ffocus-scroll","Focus & scroll on invalid submit",": server errors plug into the same focus pull as schema errors.",[1162,1163,1164],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":61,"searchDepth":78,"depth":78,"links":1166},[1167,1168,1169,1174,1175,1176,1177,1178,1179],{"id":53,"depth":78,"text":54},{"id":129,"depth":78,"text":130},{"id":302,"depth":78,"text":303,"children":1170},[1171,1172,1173],{"id":307,"depth":84,"text":308},{"id":439,"depth":84,"text":440},{"id":526,"depth":84,"text":527},{"id":648,"depth":78,"text":649},{"id":741,"depth":78,"text":742},{"id":897,"depth":78,"text":898},{"id":1029,"depth":78,"text":1030},{"id":1083,"depth":78,"text":1084},{"id":1134,"depth":78,"text":1135},"parseApiErrors normalizes server failure responses into ValidationError[] that setFieldErrors mounts into the form. One parser, three envelope shapes, both structured and bare-string entries, discriminated result type.","md",{},[1184,1187,1190,1193],{"label":1185,"value":1186},"Category","Helper",{"label":1188,"value":1189,"kind":21},"Signature","parseApiErrors(envelope, { formKey })",{"label":1191,"value":1192,"kind":21},"Returns","{ ok, errors, rejected? }",{"label":1194,"value":1195,"kind":21},"Mount with","setFieldErrors \u002F addFieldErrors",true,"\u002Fdocs\u002Fserver-and-ssr\u002Fparse-api-errors",{"title":5,"description":1180},null,"docs\u002Fserver-and-ssr\u002Fparse-api-errors","MqtJ9bYKmbdWdyAUTqFhL4CENkW-Ue4HtdZWGWp5PII",1780949762123]