Skip to main content

Missing Service Error

Error: Service not found: ProviderService Cause: A required layer wasn’t provided. Fix: Compose and provide all required layers:
import { Layer } from 'effect'
import { Provider, HttpTransport } from 'voltaire-effect'

// ❌ Missing layer
await Effect.runPromise(program)

// ✅ Compose layers first, then provide once
const ProviderLayer = Provider.pipe(
  Layer.provide(HttpTransport('https://eth.llamarpc.com'))
)

await Effect.runPromise(program.pipe(Effect.provide(ProviderLayer)))
Check the type signature—it shows what’s needed:
// Effect<bigint, GetBalanceError, ProviderService>
//                              ↑ needs this

Wrong Layer Order

Symptom: Type errors or runtime failures when composing layers. Cause: Layers that depend on other layers must be provided in correct order. Fix: Compose layers properly with dependencies:
import { Layer } from 'effect'

// ✅ Compose layers with their dependencies
const CryptoLayer = Layer.mergeAll(Secp256k1Live, KeccakLive)
const TransportLayer = HttpTransport(rpcUrl)
const ProviderLayer = Provider.pipe(Layer.provide(TransportLayer))

const DepsLayer = Layer.mergeAll(LocalAccount(privateKey), ProviderLayer, CryptoLayer)
const SignerLayer = Signer.Live.pipe(Layer.provide(DepsLayer))

Forgot to Yield

Error: Program doesn’t execute as expected. Cause: Missing yield* on Effect operations.
import { getBlockNumber } from 'voltaire-effect'

// ❌ Returns Effect, doesn't execute
Effect.gen(function* () {
  getBlockNumber()  // Missing yield*!
})

// ✅ Yield all Effects
Effect.gen(function* () {
  return yield* getBlockNumber()
})

Mixed Async Styles

Error: yield* cannot be used with Promise Cause: Using await inside Effect.gen.
// ❌ Don't use await
Effect.gen(function* () {
  const result = await somePromise()  // Wrong!
})

// ✅ Wrap promises with Effect.promise
Effect.gen(function* () {
  const result = yield* Effect.promise(() => somePromise())
})

ParseError on Valid Input

Cause: Input format doesn’t match schema expectations.
// ❌ Missing 0x prefix
S.decodeSync(Address.Hex)('742d35Cc6634C0532925a3b844Bc9e7595f251e3')

// ✅ Include 0x prefix
S.decodeSync(Address.Hex)('0x742d35Cc6634C0532925a3b844Bc9e7595f251e3')
Fix: Check schema requirements:
  • Address.Hex expects 0x + 40 hex chars
  • Hash.Hex expects 0x + 64 hex chars
  • Uint.Uint256Hex expects 0x + hex (any length up to 64)

Checksummed Address Requires KeccakService

Error: Service not found: KeccakService when encoding Address.Checksummed. Cause: EIP-55 checksumming requires Keccak256 hashing.
// ❌ Missing KeccakLive
const checksummed = await Effect.runPromise(
  S.encode(Address.Checksummed)(addr)
)

// ✅ Provide KeccakLive
const checksummed = await Effect.runPromise(
  S.encode(Address.Checksummed)(addr).pipe(
    Effect.provide(KeccakLive)
  )
)

RPC Timeout

Error: TransportError: Request timed out Fix: Increase timeout or add retry:
// Increase timeout
HttpTransport({
  url: 'https://eth.llamarpc.com',
  timeout: 60_000  // 60 seconds (ms)
})

// Add retry
program.pipe(
  Effect.retry(Schedule.exponential('100 millis').pipe(Schedule.recurs(5))),
  Effect.timeout(Duration.seconds(30))
)

Rate Limited

Error: TransportError: 429 Too Many Requests Fix: Use rate limiter or reduce concurrency:
// Limit concurrency
Effect.all(operations, { concurrency: 3 })

// Or use RateLimiter service
Effect.provide(RateLimiter({ requestsPerSecond: 10 }))

HD Wallet Not Working

Error: FFI not available or similar. Cause: HD Wallet requires native FFI, not available in WASM. Fix: Run in Node.js/Bun with FFI support, not browser or WASM environment.

Transaction Underpriced

Error: SignerError: transaction underpriced Fix: Increase gas price or use EIP-1559 with higher priority fee:
yield* signer.sendTransaction({
  to,
  value,
  maxPriorityFeePerGas: 2_000_000_000n,  // 2 gwei tip
  maxFeePerGas: 100_000_000_000n          // 100 gwei max
})

Nonce Too Low

Error: SignerError: nonce too low Cause: Transaction with this nonce already confirmed, or pending transaction uses same nonce. Fix: Let the signer auto-fetch nonce (default), or manage manually:
import { sendTransaction, getTransactionCount } from 'voltaire-effect'

// Auto nonce (recommended)
yield* sendTransaction({ to, value })

// Manual nonce
const nonce = yield* getTransactionCount(address, 'pending')
yield* sendTransaction({ to, value, nonce })

Contract Reverted

Error: ContractCallError: execution reverted Fix: Check revert reason and contract state:
program.pipe(
  Effect.catchTag('ContractCallError', (e) => {
    console.error('Revert reason:', e.message)
    // Check: sufficient balance, approvals, contract state
    return Effect.fail(e)
  })
)

// Simulate first to catch reverts
const success = yield* contract.simulate.transfer(to, amount)
if (!success) {
  // Handle failure before sending
}

Type Mismatch: String vs AddressType

Error: Type 'string' is not assignable to type 'AddressType' Cause: voltaire-effect uses branded Uint8Array, not strings.
import { getBalance } from 'voltaire-effect'

// ❌ String address
const balance = yield* getBalance('0x...')

// ✅ Decode first (or use string directly - API accepts both)
const addr = S.decodeSync(Address.Hex)('0x...')
const balance = yield* getBalance(addr)

// ✅ Or use hex string directly (getBalance accepts AddressInput)
const balance = yield* getBalance('0x...', 'latest')

Debug Effect Execution

Add logging to trace execution:
import { getBlockNumber } from 'voltaire-effect'

const program = Effect.gen(function* () {
  yield* Effect.log('Starting...')

  const block = yield* getBlockNumber()
  yield* Effect.log(`Block: ${block}`)

  return block
})

Inspect Full Error

import { Effect, Cause, Exit } from 'effect'

const exit = await Effect.runPromiseExit(program)

if (Exit.isFailure(exit)) {
  console.log(Cause.pretty(exit.cause))
}

See Also