[{"data":1,"prerenderedAt":855},["ShallowReactive",2],{"page:\u002Fextensions\u002Fkubb-labs\u002Fplugin-fetch\u002Fguides\u002Ferror-handling":3},{"id":4,"title":5,"body":6,"description":596,"extension":844,"frontmatter":845,"heading":5,"meta":846,"navigation":847,"order":848,"path":849,"seo":850,"sitemap":851,"source":4,"stem":852,"__hash__":853,"hasPopups":854},"extensions\u002Fextensions\u002Fkubb-labs\u002Fplugin-fetch\u002Fguides\u002Ferror-handling.md","Handle errors",{"type":7,"value":8,"toc":836},"minimark",[9,22,28,187,198,201,344,362,367,387,478,482,491,582,588,592,611,687,693,697,710,805,817],[10,11,12,13,17,18,21],"p",{},"Handle failures from the client Kubb generates. Catch a ",[14,15,16],"code",{},"ResponseError",", switch to a returned error result with ",[14,19,20],{},"throwOnError",", and tell a network failure apart from a 4xx or 5xx response.",[10,23,24,25,27],{},"The generated client throws a ",[14,26,16],{}," on non-2xx responses by default:",[29,30,34],"pre",{"language":31,"code":32,"class":33},"typescript","import { getPetById } from '.\u002Fgen\u002Fclients\u002FgetPetById'\nimport { ResponseError } from '.\u002Fgen\u002F.kubb\u002Fclient'\n\ntry {\n  const { data } = await getPetById({ path: { petId: 1 } })\n  console.info(data.name)\n} catch (error) {\n  if (ResponseError.is(error)) {\n    console.error(error.status) \u002F\u002F 404\n    console.error(error.data) \u002F\u002F the parsed error body\n  }\n}","shiki shiki-themes github-light github-dark dark:github-dark",[14,35,37,57,58,57,70,57,72,57,80,57,114,57,125,57,136,57,150,57,165,57,177,57,182],{"class":36},"language-typescript",[38,39,42,46,50,53],"span",{"class":40,"style":41},"line","display: inline",[38,43,45],{"style":44},"color:#D73A49;--shiki-dark:#F97583","import",[38,47,49],{"style":48},"color:#24292E;--shiki-dark:#E1E4E8"," { getPetById } ",[38,51,52],{"style":44},"from",[38,54,56],{"style":55},"color:#032F62;--shiki-dark:#9ECBFF"," '.\u002Fgen\u002Fclients\u002FgetPetById'","\n",[38,59,60,62,65,67],{"class":40,"style":41},[38,61,45],{"style":44},[38,63,64],{"style":48}," { ResponseError } ",[38,66,52],{"style":44},[38,68,69],{"style":55}," '.\u002Fgen\u002F.kubb\u002Fclient'",[38,71],{"class":40,"style":41},[38,73,74,77],{"class":40,"style":41},[38,75,76],{"style":44},"try",[38,78,79],{"style":48}," {",[38,81,82,85,88,92,95,98,101,105,108,111],{"class":40,"style":41},[38,83,84],{"style":44},"  const",[38,86,87],{"style":48}," { ",[38,89,91],{"style":90},"color:#005CC5;--shiki-dark:#79B8FF","data",[38,93,94],{"style":48}," } ",[38,96,97],{"style":44},"=",[38,99,100],{"style":44}," await",[38,102,104],{"style":103},"color:#6F42C1;--shiki-dark:#B392F0"," getPetById",[38,106,107],{"style":48},"({ path: { petId: ",[38,109,110],{"style":90},"1",[38,112,113],{"style":48}," } })",[38,115,116,119,122],{"class":40,"style":41},[38,117,118],{"style":48},"  console.",[38,120,121],{"style":103},"info",[38,123,124],{"style":48},"(data.name)",[38,126,127,130,133],{"class":40,"style":41},[38,128,129],{"style":48},"} ",[38,131,132],{"style":44},"catch",[38,134,135],{"style":48}," (error) {",[38,137,138,141,144,147],{"class":40,"style":41},[38,139,140],{"style":44},"  if",[38,142,143],{"style":48}," (ResponseError.",[38,145,146],{"style":103},"is",[38,148,149],{"style":48},"(error)) {",[38,151,152,155,158,161],{"class":40,"style":41},[38,153,154],{"style":48},"    console.",[38,156,157],{"style":103},"error",[38,159,160],{"style":48},"(error.status) ",[38,162,164],{"style":163},"color:#6A737D;--shiki-dark:#6A737D","\u002F\u002F 404",[38,166,167,169,171,174],{"class":40,"style":41},[38,168,154],{"style":48},[38,170,157],{"style":103},[38,172,173],{"style":48},"(error.data) ",[38,175,176],{"style":163},"\u002F\u002F the parsed error body",[38,178,179],{"class":40,"style":41},[38,180,181],{"style":48},"  }",[38,183,184],{"class":40,"style":41},[38,185,186],{"style":48},"}",[10,188,189,190,193,194,197],{},"The ",[14,191,192],{},"message"," holds the HTTP method and the URL without its query string, so query credentials stay out of logs. For example: ",[14,195,196],{},"GET https:\u002F\u002Fapi.example.com\u002Fpets\u002F1 failed with status 404 Not Found",".",[10,199,200],{},"The error exposes these fields:",[29,202,204],{"language":31,"code":203,"class":33},"class ResponseError extends Error {\n  static is(error: unknown): error is ResponseError\u003Cunknown, unknown, unknown>\n  data: TError \u002F\u002F the parsed error body\n  status: number\n  statusText: string\n  contentType: string | undefined\n  request: Request\n  response: Response\n}",[14,205,206,57,222,57,272,57,285,57,295,57,305,57,320,57,330,57,340],{"class":36},[38,207,208,211,214,217,220],{"class":40,"style":41},[38,209,210],{"style":44},"class",[38,212,213],{"style":103}," ResponseError",[38,215,216],{"style":44}," extends",[38,218,219],{"style":103}," Error",[38,221,79],{"style":48},[38,223,224,227,230,233,236,239,242,245,247,250,252,254,257,260,263,265,267,269],{"class":40,"style":41},[38,225,226],{"style":44},"  static",[38,228,229],{"style":103}," is",[38,231,232],{"style":48},"(",[38,234,157],{"style":235},"color:#E36209;--shiki-dark:#FFAB70",[38,237,238],{"style":44},":",[38,240,241],{"style":90}," unknown",[38,243,244],{"style":48},")",[38,246,238],{"style":44},[38,248,249],{"style":235}," error",[38,251,229],{"style":44},[38,253,213],{"style":103},[38,255,256],{"style":48},"\u003C",[38,258,259],{"style":90},"unknown",[38,261,262],{"style":48},", ",[38,264,259],{"style":90},[38,266,262],{"style":48},[38,268,259],{"style":90},[38,270,271],{"style":48},">",[38,273,274,277,279,282],{"class":40,"style":41},[38,275,276],{"style":235},"  data",[38,278,238],{"style":44},[38,280,281],{"style":103}," TError",[38,283,284],{"style":163}," \u002F\u002F the parsed error body",[38,286,287,290,292],{"class":40,"style":41},[38,288,289],{"style":235},"  status",[38,291,238],{"style":44},[38,293,294],{"style":90}," number",[38,296,297,300,302],{"class":40,"style":41},[38,298,299],{"style":235},"  statusText",[38,301,238],{"style":44},[38,303,304],{"style":90}," string",[38,306,307,310,312,314,317],{"class":40,"style":41},[38,308,309],{"style":235},"  contentType",[38,311,238],{"style":44},[38,313,304],{"style":90},[38,315,316],{"style":44}," |",[38,318,319],{"style":90}," undefined",[38,321,322,325,327],{"class":40,"style":41},[38,323,324],{"style":235},"  request",[38,326,238],{"style":44},[38,328,329],{"style":103}," Request",[38,331,332,335,337],{"class":40,"style":41},[38,333,334],{"style":235},"  response",[38,336,238],{"style":44},[38,338,339],{"style":103}," Response",[38,341,342],{"class":40,"style":41},[38,343,186],{"style":48},[10,345,346,347,349,350,353,354,357,358,361],{},"Each generated package bundles its own ",[14,348,16],{}," class, so narrow with ",[14,351,352],{},"ResponseError.is(error)"," instead of ",[14,355,356],{},"instanceof",". ",[14,359,360],{},"is()"," checks the error name.",[363,364,366],"h2",{"id":365},"return-the-error-instead","Return the error instead",[10,368,369,370,373,374,376,377,379,380,383,384,386],{},"Pass ",[14,371,372],{},"throwOnError: false"," and the call resolves for every documented status. The result is a\ndiscriminated union: ",[14,375,91],{}," is set on success and ",[14,378,157],{}," on failure. Branch on ",[14,381,382],{},"status",",\nbecause an empty error body can leave ",[14,385,157],{}," empty or undefined.",[29,388,390],{"language":31,"code":389,"class":33},"const result = await getPetById({ path: { petId: 1 }, throwOnError: false })\n\nif (result.status === 200) {\n  console.info(result.data.name)\n}\nif (result.status !== 200) {\n  console.error(result.status, result.error)\n}",[14,391,392,57,420,57,422,57,439,57,448,57,452,57,465,57,474],{"class":36},[38,393,394,397,400,403,405,407,409,411,414,417],{"class":40,"style":41},[38,395,396],{"style":44},"const",[38,398,399],{"style":90}," result",[38,401,402],{"style":44}," =",[38,404,100],{"style":44},[38,406,104],{"style":103},[38,408,107],{"style":48},[38,410,110],{"style":90},[38,412,413],{"style":48}," }, throwOnError: ",[38,415,416],{"style":90},"false",[38,418,419],{"style":48}," })",[38,421],{"class":40,"style":41},[38,423,424,427,430,433,436],{"class":40,"style":41},[38,425,426],{"style":44},"if",[38,428,429],{"style":48}," (result.status ",[38,431,432],{"style":44},"===",[38,434,435],{"style":90}," 200",[38,437,438],{"style":48},") {",[38,440,441,443,445],{"class":40,"style":41},[38,442,118],{"style":48},[38,444,121],{"style":103},[38,446,447],{"style":48},"(result.data.name)",[38,449,450],{"class":40,"style":41},[38,451,186],{"style":48},[38,453,454,456,458,461,463],{"class":40,"style":41},[38,455,426],{"style":44},[38,457,429],{"style":48},[38,459,460],{"style":44},"!==",[38,462,435],{"style":90},[38,464,438],{"style":48},[38,466,467,469,471],{"class":40,"style":41},[38,468,118],{"style":48},[38,470,157],{"style":103},[38,472,473],{"style":48},"(result.status, result.error)",[38,475,476],{"class":40,"style":41},[38,477,186],{"style":48},[363,479,481],{"id":480},"set-the-generated-default","Set the generated default",[10,483,484,485,488,489,238],{},"Set ",[14,486,487],{},"throwOnErrorDefault"," on the client plugin to choose how generated operations handle non-2xx\nresponses by default. Each call can still override that setting with ",[14,490,20],{},[29,492,494],{"language":31,"code":493,"class":33},"import { pluginFetch } from '@kubb\u002Fplugin-fetch'\n\npluginFetch({ throwOnErrorDefault: false })\n\n\u002F\u002F resolves with an error result\nconst list = await searchPets({ query: { status: 'available' } })\n\n\u002F\u002F opt one call back into throwing\nconst pet = await getPetById({ path: { petId: 1 }, throwOnError: true })",[14,495,496,57,508,57,510,57,522,57,524,57,529,57,551,57,553,57,558],{"class":36},[38,497,498,500,503,505],{"class":40,"style":41},[38,499,45],{"style":44},[38,501,502],{"style":48}," { pluginFetch } ",[38,504,52],{"style":44},[38,506,507],{"style":55}," '@kubb\u002Fplugin-fetch'",[38,509],{"class":40,"style":41},[38,511,512,515,518,520],{"class":40,"style":41},[38,513,514],{"style":103},"pluginFetch",[38,516,517],{"style":48},"({ throwOnErrorDefault: ",[38,519,416],{"style":90},[38,521,419],{"style":48},[38,523],{"class":40,"style":41},[38,525,526],{"class":40,"style":41},[38,527,528],{"style":163},"\u002F\u002F resolves with an error result",[38,530,531,533,536,538,540,543,546,549],{"class":40,"style":41},[38,532,396],{"style":44},[38,534,535],{"style":90}," list",[38,537,402],{"style":44},[38,539,100],{"style":44},[38,541,542],{"style":103}," searchPets",[38,544,545],{"style":48},"({ query: { status: ",[38,547,548],{"style":55},"'available'",[38,550,113],{"style":48},[38,552],{"class":40,"style":41},[38,554,555],{"class":40,"style":41},[38,556,557],{"style":163},"\u002F\u002F opt one call back into throwing",[38,559,560,562,565,567,569,571,573,575,577,580],{"class":40,"style":41},[38,561,396],{"style":44},[38,563,564],{"style":90}," pet",[38,566,402],{"style":44},[38,568,100],{"style":44},[38,570,104],{"style":103},[38,572,107],{"style":48},[38,574,110],{"style":90},[38,576,413],{"style":48},[38,578,579],{"style":90},"true",[38,581,419],{"style":48},[10,583,584,587],{},[14,585,586],{},"client.setConfig({ throwOnError })"," does not change this default, because each generated\noperation passes its own value. Set it on the plugin.",[363,589,591],{"id":590},"network-failures-still-throw","Network failures still throw",[593,594,596,597,599,600,603,604,606,607,610],"blockquote",{"as":595},"important","",[14,598,20],{}," only applies when the server answers. A dropped connection, a DNS failure, or an\naborted ",[14,601,602],{},"AbortSignal"," always rejects. A ",[14,605,16],{}," means the server answered with a non-2xx\nstatus, and a ",[14,608,609],{},"ParseError"," means a body failed validation. Any other error usually means the\nrequest never completed.",[29,612,614],{"language":31,"code":613,"class":33},"try {\n  const { data } = await getPetById({ path: { petId: 1 }, throwOnError: false })\n  use(data)\n} catch (error) {\n  \u002F\u002F not a ResponseError: the request never completed\n  console.error('request failed to send', error)\n}",[14,615,616,57,622,57,648,57,656,57,664,57,669,57,683],{"class":36},[38,617,618,620],{"class":40,"style":41},[38,619,76],{"style":44},[38,621,79],{"style":48},[38,623,624,626,628,630,632,634,636,638,640,642,644,646],{"class":40,"style":41},[38,625,84],{"style":44},[38,627,87],{"style":48},[38,629,91],{"style":90},[38,631,94],{"style":48},[38,633,97],{"style":44},[38,635,100],{"style":44},[38,637,104],{"style":103},[38,639,107],{"style":48},[38,641,110],{"style":90},[38,643,413],{"style":48},[38,645,416],{"style":90},[38,647,419],{"style":48},[38,649,650,653],{"class":40,"style":41},[38,651,652],{"style":103},"  use",[38,654,655],{"style":48},"(data)",[38,657,658,660,662],{"class":40,"style":41},[38,659,129],{"style":48},[38,661,132],{"style":44},[38,663,135],{"style":48},[38,665,666],{"class":40,"style":41},[38,667,668],{"style":163},"  \u002F\u002F not a ResponseError: the request never completed",[38,670,671,673,675,677,680],{"class":40,"style":41},[38,672,118],{"style":48},[38,674,157],{"style":103},[38,676,232],{"style":48},[38,678,679],{"style":55},"'request failed to send'",[38,681,682],{"style":48},", error)",[38,684,685],{"class":40,"style":41},[38,686,186],{"style":48},[10,688,689,690,692],{},"Pass an ",[14,691,602],{}," to cancel a request. The call rejects with the abort reason.",[363,694,696],{"id":695},"validation-failures","Validation failures",[10,698,699,700,703,704,706,707,238],{},"When you turn on the ",[14,701,702],{},"validator"," option, a body that does not match its Zod schema throws a\n",[14,705,609],{}," instead of returning. It carries the schema's raw ",[14,708,709],{},"issues",[29,711,713],{"language":31,"code":712,"class":33},"import { ParseError } from '.\u002Fgen\u002F.kubb\u002FstandardSchema'\n\ntry {\n  const { data } = await getPetById({ path: { petId: 1 } })\n  use(data)\n} catch (error) {\n  if (error instanceof ParseError) {\n    console.error(error.issues) \u002F\u002F [{ message, path }]\n  }\n}",[14,714,715,57,727,57,729,57,735,57,757,57,763,57,771,57,785,57,797,57,801],{"class":36},[38,716,717,719,722,724],{"class":40,"style":41},[38,718,45],{"style":44},[38,720,721],{"style":48}," { ParseError } ",[38,723,52],{"style":44},[38,725,726],{"style":55}," '.\u002Fgen\u002F.kubb\u002FstandardSchema'",[38,728],{"class":40,"style":41},[38,730,731,733],{"class":40,"style":41},[38,732,76],{"style":44},[38,734,79],{"style":48},[38,736,737,739,741,743,745,747,749,751,753,755],{"class":40,"style":41},[38,738,84],{"style":44},[38,740,87],{"style":48},[38,742,91],{"style":90},[38,744,94],{"style":48},[38,746,97],{"style":44},[38,748,100],{"style":44},[38,750,104],{"style":103},[38,752,107],{"style":48},[38,754,110],{"style":90},[38,756,113],{"style":48},[38,758,759,761],{"class":40,"style":41},[38,760,652],{"style":103},[38,762,655],{"style":48},[38,764,765,767,769],{"class":40,"style":41},[38,766,129],{"style":48},[38,768,132],{"style":44},[38,770,135],{"style":48},[38,772,773,775,778,780,783],{"class":40,"style":41},[38,774,140],{"style":44},[38,776,777],{"style":48}," (error ",[38,779,356],{"style":44},[38,781,782],{"style":103}," ParseError",[38,784,438],{"style":48},[38,786,787,789,791,794],{"class":40,"style":41},[38,788,154],{"style":48},[38,790,157],{"style":103},[38,792,793],{"style":48},"(error.issues) ",[38,795,796],{"style":163},"\u002F\u002F [{ message, path }]",[38,798,799],{"class":40,"style":41},[38,800,181],{"style":48},[38,802,803],{"class":40,"style":41},[38,804,186],{"style":48},[10,806,807,808,810,811,813,814,816],{},"A ",[14,809,609],{}," reports schema validation issues. A ",[14,812,16],{}," reports a non-2xx status. With ",[14,815,372],{},", the error body is checked against its own error schema.",[10,818,819,820,823,824,826,827,829,830,832,833,835],{},"Calling ",[14,821,822],{},".unwrap()"," on a ",[14,825,372],{}," call turns that same ",[14,828,157],{}," into a rejection, so a\n",[14,831,76],{},"\u002F",[14,834,132],{}," works there too.",{"title":596,"searchDepth":837,"depth":837,"links":838},5,[839,841,842,843],{"id":365,"depth":840,"text":366},2,{"id":480,"depth":840,"text":481},{"id":590,"depth":840,"text":591},{"id":695,"depth":840,"text":696},"md",{},{},true,null,"\u002Fextensions\u002Fkubb-labs\u002Fplugin-fetch\u002Fguides\u002Ferror-handling",{"title":5},{"loc":849},"extensions\u002Fkubb-labs\u002Fplugin-fetch\u002Fguides\u002Ferror-handling","XU8bbwwXwyUYBJLXq4QjksZPSLR0qQYPUOUiP5rSPp4",false,1792782254523]