---
sidebar_position: 11
---

import Tabs from "@theme/Tabs"
import TabItem from "@theme/TabItem"

# Troubleshooting

If these suggestions do not work, feel free to ask in the [Semaphore Discussions](https://github.com/semaphore-protocol/semaphore/discussions) or in the `dev-chat` channel in the [Semaphore Discord](https://semaphore.pse.dev/discord).

## Using Semaphore in the frontend

Semaphore works with any JavaScript frontend framework, but the [`@semaphore-protocol/proof`](https://github.com/semaphore-protocol/semaphore/tree/v3.15.2/packages/proof) package is using [snarkjs](https://github.com/iden3/snarkjs), which uses Node.js modules which are not compatible with frontend frameworks and there are some changes that we need to do to make it work on the client side.

### Semaphore with Nextjs

You will see an error like this:

```
Module not found: Can't resolve 'fs'
```

To solve this, in your `next.config.js` file, inside the `nextConfig` object, add:

```javascript
webpack: (config, { isServer }) => {
    if (!isServer) {
      config.resolve.fallback = {
        fs: false
      }
    }

    return config
  }
```

Your `next.config.js` file would be something like this:

```javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
    reactStrictMode: true,
    webpack: (config, { isServer }) => {
        if (!isServer) {
            config.resolve.fallback = {
                fs: false
            }
        }

        return config
    }
}

module.exports = nextConfig
```

### Semaphore with React + Vite or Vuejs + Vite

You will see an error like this:

```bash
readman.js:43 Uncaught ReferenceError: process is not defined
    at stringToBase64 (threadman.js:43:5)
    at threadman.js:50:22
```

To solve that:

1- Install `@esbuild-plugins/node-globals-polyfill` and `@esbuild-plugins/node-modules-polyfill`

<Tabs
  defaultValue="npm"
  groupId="package-managers"
  values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
{label: 'pnpm', value: 'pnpm'}
]}
>
  <TabItem value="npm">
    ```bash
    npm install @esbuild-plugins/node-globals-polyfill
    ```
  </TabItem>

  <TabItem value="yarn">
    ```bash
    yarn add @esbuild-plugins/node-globals-polyfill
    ```
  </TabItem>

  <TabItem value="pnpm">
    ```bash
    pnpm add @esbuild-plugins/node-globals-polyfill
    ```
  </TabItem>
</Tabs>

<Tabs
  defaultValue="npm"
  groupId="package-managers"
  values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
{label: 'pnpm', value: 'pnpm'}
]}
>
  <TabItem value="npm">
    ```bash
    npm install @esbuild-plugins/node-modules-polyfill
    ```
  </TabItem>

  <TabItem value="yarn">
    ```bash
    yarn add @esbuild-plugins/node-modules-polyfill
    ```
  </TabItem>

  <TabItem value="pnpm">
    ```bash
    pnpm add @esbuild-plugins/node-modules-polyfill
    ```
  </TabItem>
</Tabs>

2- Modify the `vite.config.ts` to add them:

```typescript
import { NodeGlobalsPolyfillPlugin } from "@esbuild-plugins/node-globals-polyfill"
import { NodeModulesPolyfillPlugin } from "@esbuild-plugins/node-modules-polyfill"
```

and in `defineConfig` add:

```typescript
optimizeDeps: {
    esbuildOptions: {
        // Enable esbuild polyfill plugins
        plugins: [
            NodeGlobalsPolyfillPlugin({
                process: true,
                buffer: true
            }),
            NodeModulesPolyfillPlugin()
        ]
    }
}
```

Your `vite.config.ts` should be something like:

```typescript
import { fileURLToPath, URL } from "node:url"

import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"

import { NodeGlobalsPolyfillPlugin } from "@esbuild-plugins/node-globals-polyfill"
import { NodeModulesPolyfillPlugin } from "@esbuild-plugins/node-modules-polyfill"

// https://vitejs.dev/config/
export default defineConfig({
    plugins: [vue()],
    resolve: {
        alias: {
            "@": fileURLToPath(new URL("./src", import.meta.url))
        }
    },
    optimizeDeps: {
        esbuildOptions: {
            // Enable esbuild polyfill plugins
            plugins: [
                NodeGlobalsPolyfillPlugin({
                    process: true,
                    buffer: true
                }),
                NodeModulesPolyfillPlugin()
            ]
        }
    }
})
```

:::info
In case of React with Vite, if you see a red wavy underline on every Semaphore module which says `Could not find a declaration file for module ...`, change the `moduleResolution` from `bundler` to `Node` in the `tsconfig.json` file inside `compilerOptions`.

Your `tsconfig.json` file would be something like this:

```json
{
    "compilerOptions": {
        "target": "ESNext",
        "lib": ["DOM", "DOM.Iterable", "ESNext"],
        "module": "ESNext",
        "skipLibCheck": true,

        /* Bundler mode */
        "moduleResolution": "Node",
        "allowImportingTsExtensions": true,
        "resolveJsonModule": true,
        "isolatedModules": true,
        "noEmit": true,
        "jsx": "react-jsx",

        /* Linting */
        "strict": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "noFallthroughCasesInSwitch": true
    },
    "include": ["src"],
    "references": [{ "path": "./tsconfig.node.json" }]
}
```

:::

## Semaphore Groups

### Creating a Group

When you create a group and the transaction is reverted, make sure that the group id you are using does not exist on the network you are using.

To check that, you can use the [Semaphore CLI](https://github.com/semaphore-protocol/semaphore/tree/v3.15.2/packages/cli) with the command `get-groups` and the network you are using and then, make sure that your group id is not part of that list. You can also use the [Semaphore explorer](https://explorer.semaphore.pse.dev/).

## Semaphore Proofs

### Transaction reverted when using the same external nullifier

When you generate a proof using the same external nullifier you used to verify a proof before, the transaction will be reverted because that external nullifier was already used. If you want to send and verify several proofs from the same identity, you should use a different external nullifier each time you generate a proof.
