npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

web3-error-humanizer

v2.8.0

Published

Humanize cryptic Web3 and DEX errors into user-friendly messages. 790+ local patterns, optional AI fallback.

Readme

web3-error-humanizer

A local-first developer toolkit for Web3 errors -- structured classification, severity, actionable suggestions, and 790+ local patterns. Optional AI fallback.

npm version License: MIT TypeScript

Why?

When a DEX swap fails, your users see this:

execution reverted: INSUFFICIENT_OUTPUT_AMOUNT
Error: Pancake: K
ContractFunctionRevertedError: UniswapV2: LOCKED

With this library, they see this instead:

Price moved too much. Try increasing your slippage tolerance.
Low liquidity for this pair. Try a smaller swap amount.
This pair is currently locked. Try again shortly.

One import. Zero config. No API key required.

import { humanizeError } from "web3-error-humanizer";

const message = humanizeError(error);
// "Price moved too much. Try increasing your slippage tolerance."

EVM-only apps can import a smaller dictionary (generic + wallets + EVM + bridges). The default import still includes every chain.

import { humanizeErrorDetailed } from "web3-error-humanizer/evm";

Or get full structured output for building smart UIs:

import {
  humanizeErrorDetailed,
  isRecoverable,
  classifyError,
} from "web3-error-humanizer";

const result = humanizeErrorDetailed(error);
// {
//   message:     "Price moved too much. Try increasing your slippage tolerance.",
//   category:    "slippage",
//   severity:    "warning",
//   suggestion:  "Increase your slippage tolerance or try a smaller amount.",
//   recoverable: true,
//   source:      "local",
//   matchedKey:  "INSUFFICIENT_OUTPUT_AMOUNT",
//   rawMessage:  "INSUFFICIENT_OUTPUT_AMOUNT"
// }

if (result.category === "insufficient_allowance") showApproveButton();
if (result.category === "chain_mismatch") showSwitchNetworkButton();
if (result.recoverable) showRetryButton();

Apps with i18n should keep the English message as a fallback and map result.category or result.matchedKey through their own translator.

Why Developers Like It

  • Useful without AI -- the default import is local-only, fast, and has zero runtime dependencies
  • Structured, not just pretty strings -- categories, severity, suggestions, and recoverability let you build real product UX
  • Optional AI instead of forced AI -- unknown errors can use the /ai entry point, but the core package never requires an API key
  • Safe to bundle -- ESM + CommonJS exports and TypeScript types. Use /evm or /solana to skip unused chain dictionaries.

Features

  • 790+ local error patterns -- exact, token, and scored substring matches, no API calls needed
  • Structured error output -- category, severity, suggestion, recoverability, and matchedKey for every error
  • 16 error categories -- user_rejection, insufficient_funds, slippage, gas, network, bridge, and more
  • Zero dependencies -- the main entry point has no runtime dependencies
  • Isolated instances -- createHumanizer({ chain, patterns }) for Next.js, tests, and multi-chain apps
  • AI fallback -- unknown errors optionally analyzed by GPT-4o-mini (separate import, not for swap confirm)
  • viem-compatible -- deep error extraction for nested blockchain errors (viem is optional)
  • Extensible -- add your own patterns with createHumanizer({ patterns }) or process-wide addPattern()
  • Dual module -- ESM and CommonJS, full TypeScript types included

Supported Protocols

| Protocol | Errors Covered | | ---------------------------------- | -------------------------------------------------------------------------- | | Uniswap V2/V3/V4 | K, INSUFFICIENT_OUTPUT_AMOUNT, EXPIRED, LOCKED, SPL, Hook errors, and more | | PancakeSwap | K, INSUFFICIENT_LIQUIDITY, TRANSFER_FAILED, etc. | | SushiSwap | K, INSUFFICIENT_OUTPUT_AMOUNT, EXPIRED | | Curve Finance | Insufficient output/input, slippage, math errors | | Balancer | Insufficient liquidity, paused pools, swap disabled | | 1inch | minReturn, ReturnAmountIsNotEnough, insufficient liquidity | | DODO | Insufficient output/input, liquidity errors | | KyberSwap | Insufficient output/input, liquidity errors | | Aave V3 | VL_* errors (borrowing, supply caps, health factors) | | Account Abstraction (ERC-4337) | AA10-AA51 (EntryPoint errors, paymaster, validation) | | Solana / Jupiter | Program errors, slippage, compute budget, blockhash | | LayerZero | Bridge errors, token unavailability, message blocking | | Li.Fi / Stargate | Route errors, slippage, amount limits | | Arbitrum / Optimism | Retryable tickets, L2 execution, fee errors | | MetaMask/EIP-1193 | 4001, 4100, 4900, -32603, and all standard codes | | WalletConnect/Reown | USER_REJECTED, SESSION_EXPIRED, APKT001-APKT010 | | Gnosis Safe | GS000-GS031 (initialization, signatures, owners) | | ERC20/721/1155 | ERC-6093 standard errors, allowance, balance, transfer | | Gas/Network | Underpriced, out of gas, timeout, replacement errors | | Hardware Wallets | Ledger, Trezor connection and signing errors | | Multi-chain Wallets | Phantom, TronLink, Sui, Aptos, TON, Bitcoin wallets |

Installation

npm install web3-error-humanizer

Also works with pnpm add and yarn add. Zero dependencies for the main entry point. Install openai separately if you want AI fallback. The published package runs on Node.js >= 20. Publishing a release with semantic-release currently needs Node.js >= 22.14.

Quick Start

Local Only (No API Key Required)

import { humanizeError } from "web3-error-humanizer";

try {
  await contract.write.swap([...]);
} catch (error) {
  const message = humanizeError(error);
  console.log(message);
  // "Price moved too much. Try increasing your slippage tolerance."
}

With AI Fallback

import { Web3ErrorHumanizer } from "web3-error-humanizer/ai";

const humanizer = new Web3ErrorHumanizer({
  openaiApiKey: process.env.OPENAI_API_KEY!,
});

try {
  await contract.write.swap([...]);
} catch (error) {
  const message = await humanizer.humanize(error);
  // Local match -> instant response
  // Unknown error -> AI generates response
}

Requires openai as a peer dependency. The /ai entry point only sends sanitized error text to OpenAI when you configure an API key. The default import never makes network requests.

Toolkit API

Error Categories

Every matched error is classified into one of 16 categories:

| Category | Description | Severity | Recoverable | | ------------------------ | --------------------------------- | --------- | ----------- | | user_rejection | User cancelled/rejected in wallet | info | Yes | | insufficient_funds | Not enough balance or gas | error | Yes | | insufficient_allowance | Token needs approval first | warning | Yes | | slippage | Price moved beyond tolerance | warning | Yes | | liquidity | Pool has no/low liquidity | error | Yes | | gas | Gas estimation or pricing failed | error | Yes | | nonce | Transaction ordering issue | warning | Yes | | network | RPC / connection problems | error | Yes | | contract_error | Smart contract reverted | error | No | | timeout | Transaction/request timed out | warning | Yes | | wallet_connection | Wallet not connected/locked | error | Yes | | chain_mismatch | Wrong network selected | warning | Yes | | protocol_limit | Supply/borrow caps, paused state | error | Yes | | signature | Signing failed | error | Yes | | bridge | Cross-chain bridge errors | error | Yes | | unknown | Unrecognized error | error | No |

Building Smart Error UIs

import {
  humanizeErrorDetailed,
  classifyError,
  isRecoverable,
  getSuggestion,
} from "web3-error-humanizer";

try {
  await sendTransaction();
} catch (err) {
  const result = humanizeErrorDetailed(err);

  showToast(result.message);

  if (result.category === "insufficient_allowance") {
    showApproveButton();
  } else if (result.category === "chain_mismatch") {
    showSwitchNetworkButton();
  } else if (result.category === "insufficient_funds") {
    showAddFundsLink();
  } else if (result.recoverable) {
    showRetryButton();
  }

  analytics.track("tx_error", {
    category: result.category,
    severity: result.severity,
    recoverable: result.recoverable,
    raw: result.rawMessage,
  });
}

Quick Classification (No Humanization)

import {
  classifyError,
  isRecoverable,
  getSuggestion,
  getErrorSeverity,
} from "web3-error-humanizer";

const category = classifyError(error); // "slippage"
const canRetry = isRecoverable(error); // true
const nextStep = getSuggestion(error); // "Increase your slippage tolerance or try a smaller amount."
const severity = getErrorSeverity(error); // "warning"

Isolated instance (recommended for apps and tests)

addPattern() mutates a process-wide registry. Prefer createHumanizer() when you need custom patterns, a chain hint, or test isolation:

import { createHumanizer } from "web3-error-humanizer";

const humanizer = createHumanizer({
  chain: "evm",
  fallbackMessage: "Swap failed. Please try again.",
  patterns: {
    MY_DEX_ERROR: { message: "This pool is paused.", category: "protocol_limit" },
  },
});

const result = humanizer.humanizeDetailed(error);

chain only narrows protocol-specific codes (for example Aave 26 vs Jupiter 0x1771). Shared wallet and rejection patterns still match. Omit chain to use the full dictionary.

Wagmi / viem

import { humanizeErrorDetailed } from "web3-error-humanizer";

try {
  await walletClient.writeContract({ /* ... */ });
} catch (error) {
  const result = humanizeErrorDetailed(error);
  toast.error(result.message);
  if (result.category === "insufficient_allowance") openApprove();
  else if (result.category === "chain_mismatch") openSwitchNetwork();
  else if (result.recoverable) showRetry();
}

Usage with Context

Provide swap context for smarter AI responses. Do not call /ai on the swap confirm path -- keep unmatched errors on the generic fallback:

const message = await humanizer.humanize(error, {
  fromToken: "USDC",
  toToken: "PEPE",
  amount: "1000",
  slippage: "0.5%",
  network: "Ethereum",
});
// "PEPE's price is changing rapidly. Increase slippage to 1-2% or try a smaller amount."

API Reference

| Function | Returns | Description | | --- | --- | --- | | humanizeError(error, fallback?) | string | Human-friendly message, or fallback | | humanizeErrorLocal(error) | string \| null | Human-friendly message, or null if no match | | humanizeErrorDetailed(error, fallback?) | HumanizedResult | Rich object with category, severity, suggestion | | classifyError(error) | ErrorCategory | Error category without humanizing | | isRecoverable(error) | boolean | Whether the user can fix this | | getSuggestion(error) | string | Actionable next step | | getErrorSeverity(error) | ErrorSeverity | "error" / "warning" / "info" | | extractRawMessage(error) | string | Raw message from any error shape | | createHumanizer(options?) | LocalHumanizer | Isolated registry with optional chain and patterns | | addPattern(key, msg, category?) | void | Add a custom pattern at runtime (process-wide) | | addPatterns(map) | void | Batch add patterns (process-wide) | | resetCustomPatterns() | void | Restore built-ins and rebuild the lookup index | | getLocalErrorCount() | number | Total patterns in registry | | hasLocalPattern(key) | boolean | Check if a pattern exists | | getLocalPatterns() | string[] | List all pattern keys |

Class-based (AI fallback): Import Web3ErrorHumanizer from web3-error-humanizer/ai. See API.md for full documentation with examples.

How It Works

flowchart TD
    A["Caught Error"] --> B["Extract Message"]
    B --> C{"Local Dictionary\n790+ patterns"}
    C -->|match found| D["Instant Response\nfree, less than 1ms"]
    C -->|no match| E{"AI configured?"}
    E -->|yes| F["OpenAI API\npaid, ~500ms"]
    E -->|no| G["Fallback Message"]
  1. Extract -- Pulls the raw error message from viem BaseError, ethers error objects, EIP-1193 codes, or plain strings
  2. Match locally -- exact phrase/code, then embedded RPC/hex tokens, then scored substring matches (category priority, then length)
  3. AI fallback -- Optional /ai import only. Unknown errors can stay on the generic fallback.
  4. Fallback -- Returns a configurable default message if nothing else matches

Limitations

  • Local matching is only as good as the current dictionary. Unknown protocol-specific errors still need new patterns. Leaving them on the fallback is fine.
  • Generic single-word keys such as TIMEOUT are exact-only so they do not hijack unrelated messages.
  • addPattern() and addPatterns() mutate a shared in-memory registry for the current process. Prefer createHumanizer({ patterns }) in apps and tests. resetCustomPatterns() restores built-ins and rebuilds the index.
  • If you use web3-error-humanizer/ai, do not pass secrets in error context that you would not want sent to your model provider.
  • For high-volume AI usage, consider caching responses for repeated errors.

Adopting later in an existing DEX (Houdini-style)

If the app already has chain-aware helpers and t() keys, do not replace that layer in one shot:

  1. Call humanizeErrorDetailed() or createHumanizer({ chain }).humanizeDetailed() beside the existing parser.
  2. If the app already has a translation key, keep it.
  3. Otherwise use category / matchedKey to drive t() and CTAs.
  4. Keep /ai off the swap confirm path.

More

License

MIT © halilatilla