{"id":"b7121db09b2c08f7","repo":"vitest-dev/vitest","slug":"snapshot-function-didn-t-throw","errorCode":null,"errorMessage":"snapshot function didn't throw","messagePattern":"snapshot function didn't throw","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/integrations/snapshot/chai.ts","lineNumber":46,"sourceCode":"  if (typeof expected !== 'function') {\n    if (!promise) {\n      throw new Error(\n        `expected must be a function, received ${typeof expected}`,\n      )\n    }\n\n    // when \"promised\", it receives thrown error\n    return expected\n  }\n\n  try {\n    expected()\n  }\n  catch (e) {\n    return e\n  }\n\n  throw new Error('snapshot function didn\\'t throw')\n}\n\nfunction getTestNames(test: Test) {\n  return {\n    filepath: test.file.filepath,\n    name: getNames(test).slice(1).join(' > '),\n    testId: test.id,\n  }\n}\n\nfunction getAssertionName(assertion: Chai.Assertion): string {\n  const name = chai.util.flag(assertion, '_name') as string | undefined\n  if (!name) {\n    throw new Error('Assertion name is not set. This is a bug in Vitest. Please, open a new issue with reproduction.')\n  }\n  return name\n}\n","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/vitest/src/integrations/snapshot/chai.ts#L28-L64","documentation":"After confirming the subject is a function, `getError` invokes it expecting it to throw so the thrown value can be snapshotted. If the function returns normally without throwing, there is no error to snapshot, so Vitest throws `snapshot function didn't throw`. This is the synchronous counterpart of `getError`; in a promise context the error is received directly instead.","triggerScenarios":"`expect(() => { /* returns normally */ }).toThrowErrorMatchingSnapshot()` / `...InlineSnapshot()` where the wrapped function does not actually throw.","commonSituations":"The function was refactored to no longer throw, the triggering input changed, or an earlier `try/catch` swallowed the error before it could propagate out of the wrapped function.","solutions":["Confirm the function still throws for the given input by running it directly first.","Update the test to use input that does throw, or switch to a non-throw snapshot matcher if the behavior changed.","Remove any internal `try/catch` that swallows the error inside the wrapped function."],"exampleFix":"// before\nexpect(() => validate('ok')).toThrowErrorMatchingSnapshot() // validate no longer throws on 'ok'\n// after\nexpect(() => validate('')).toThrowErrorMatchingSnapshot() // input that actually throws","handlingStrategy":"try-catch","validationCode":"function capturesError(fn: () => unknown): boolean {\n  try { fn(); return false } catch { return true }\n}\nif (!capturesError(() => risky())) throw new Error('function does not throw; nothing to snapshot')","typeGuard":null,"tryCatchPattern":"try {\n  risky()\n  throw new Error('expected risky() to throw before snapshotting')\n} catch (e) {\n  expect(() => { throw e }).toThrowErrorMatchingSnapshot()\n}","preventionTips":["Verify the function throws with input that triggers the error path.","Avoid swallowing errors with internal try/catch inside the wrapped function."],"tags":["snapshot","matcher","throw","assertion"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}