{"record":{"id":"cd208a7ca1973037","repo":"sveltejs/kit","slug":"cannot-throw-fail-use-return-fail","errorCode":null,"errorMessage":"Cannot \"throw fail()\". Use \"return fail()\"","messagePattern":"Cannot \"throw fail\\(\\)\"\\. Use \"return fail\\(\\)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/kit/src/runtime/server/page/actions.js","lineNumber":198,"sourceCode":"\t\tif (data instanceof ActionFailure) {\n\t\t\treturn {\n\t\t\t\ttype: 'failure',\n\t\t\t\tstatus: data.status,\n\t\t\t\tlocation,\n\t\t\t\tdata: data.data\n\t\t\t};\n\t\t} else {\n\t\t\treturn {\n\t\t\t\ttype: 'success',\n\t\t\t\tstatus: 200,\n\t\t\t\tlocation,\n\t\t\t\t// @ts-expect-error this will be removed upon serialization, so `undefined` is the same as omission\n\t\t\t\tdata\n\t\t\t};\n\t\t}\n\t} catch (e) {\n\t\treturn action_error_result(\n\t\t\te instanceof ActionFailure ? new Error('Cannot \"throw fail()\". Use \"return fail()\"') : e,\n\t\t\tlocation\n\t\t);\n\t}\n}\n\n/**\n * @param {Actions} actions\n */\nfunction check_named_default_separate(actions) {\n\tif (actions.default && Object.keys(actions).length > 1) {\n\t\tthrow new Error(\n\t\t\t'When using named actions, the default action cannot be used. See the docs for more info: https://svelte.dev/docs/kit/form-actions#named-actions'\n\t\t);\n\t}\n}\n\n/**\n * @param {RequestEvent} event","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/sveltejs/kit/blob/03f1687fe612ce3d2d9131139b5b188d9cf90c64/packages/kit/src/runtime/server/page/actions.js#L180-L216","documentation":"fail() in SvelteKit actions returns a special ActionFailure object that must be returned from the action, not thrown. If an action throws an ActionFailure (e.g. `throw fail(400, ...)`), handle_action_request replaces it with this Error to tell the developer they used the wrong control flow. Only HttpError (from error()) may be thrown.","triggerScenarios":"Writing `throw fail(400, { field: 'bad' })` inside a form action (or code called by it) instead of `return fail(400, { field: 'bad' })`; also happens when a helper that was written to `return fail()` is accidentally `throw`n, or after migrating habits from libraries where throwing result objects is idiomatic.","commonSituations":"Refactoring validation helpers where one path returns fail() and a wrapper throws it; copying error() throwing patterns into fail() usage; TypeScript not catching it because fail's throw-ability isn't statically rejected in all code paths.","solutions":["Change `throw fail(...)` to `return fail(...)` in the action and propagate the value up through helpers.","If a helper produces the failure, return it up the call chain instead of throwing.","Use `error(...)` from '@sveltejs/kit' for throwable errors, reserving fail() for form validation results.","Run `pnpm run check`/tsc to surface paths where an ActionFailure leaks into a throw."],"exampleFix":"// before\nimport { fail } from '@sveltejs/kit';\nif (!data.email) throw fail(400, { invalid: true });\n// after\nif (!data.email) return fail(400, { invalid: true });","handlingStrategy":"try-catch","validationCode":"// helper returning instead of throwing\n/** @returns {import('@sveltejs/kit').ActionFailure} */\nfunction validate(data) {\n  if (!data.email) return fail(400, { invalid: true });\n  // ...\n}","typeGuard":"function isActionFailure(e) {\n  return e instanceof ActionFailure;\n}","tryCatchPattern":"try {\n  const result = await helper();\n  if (result instanceof ActionFailure) return result; // propagate, don't throw\n} catch (e) {\n  if (e instanceof ActionFailure) return e;\n  throw error(500, 'Unexpected failure');\n}","preventionTips":["Never `throw fail(...)`; always `return fail(...)` and propagate upward.","Reserve `throw` for error() HttpErrors.","Make helpers return failures rather than throwing them.","Run type checking to catch ActionFailure leaking into throw paths."],"tags":["sveltekit","actions","fail","developer-error"],"backgroundTag":"throw-fail-in-action","analyzedSha":"03f1687fe612ce3d2d9131139b5b188d9cf90c64","analyzedAt":"2026-09-02T02:01:50.504Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}