For comprehensive debugging, see the Effect Debugging Guide.
Inspect Effect Execution
Log Steps
Copy
Ask AI
import { Effect } from 'effect'
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
})
Log With Context
Copy
Ask AI
import { getBalance } from 'voltaire-effect'
Effect.gen(function* () {
yield* Effect.logInfo('Fetching balance').pipe(
Effect.annotateLogs({ address, network: 'mainnet' })
)
return yield* getBalance(address)
})
Examine Errors
Print Full Error
Copy
Ask AI
import { Effect, Cause } from 'effect'
const exit = await Effect.runPromiseExit(program)
if (exit._tag === 'Failure') {
console.log(Cause.pretty(exit.cause))
}
Catch and Log
Copy
Ask AI
program.pipe(
Effect.catchAll((e) => {
console.error('Error:', e)
console.error('Tag:', e._tag)
return Effect.fail(e)
})
)
Tap Errors Without Handling
Copy
Ask AI
program.pipe(
Effect.tapError((e) => Effect.log(`Error occurred: ${e._tag}`))
)
Schema Parse Errors
Get Detailed Parse Error
Copy
Ask AI
import * as S from 'effect/Schema'
import { Effect } from 'effect'
S.decode(Address.Hex)('invalid').pipe(
Effect.catchTag('ParseError', (e) => {
console.log('Message:', e.message)
console.log('Issue:', JSON.stringify(e.issue, null, 2))
return Effect.fail(e)
})
)
Pretty Print Parse Error
Copy
Ask AI
import { TreeFormatter } from 'effect/ParseResult'
S.decode(Address.Hex)('0x123').pipe(
Effect.catchTag('ParseError', (e) => {
console.log(TreeFormatter.formatIssue(e.issue))
return Effect.fail(e)
})
)
Trace Execution
Add Spans
Copy
Ask AI
import { Effect } from 'effect'
import { getBalance } from 'voltaire-effect'
const fetchBalance = (address: string) =>
Effect.gen(function* () {
return yield* getBalance(address)
}).pipe(Effect.withSpan('fetchBalance', { attributes: { address } }))
const program = Effect.gen(function* () {
const { balance1, balance2 } = yield* Effect.all({
balance1: fetchBalance(addr1),
balance2: fetchBalance(addr2)
})
return { balance1, balance2 }
}).pipe(Effect.withSpan('program'))
Debug RPC Calls
Log Transport Requests
Copy
Ask AI
import { Effect, Layer } from 'effect'
import { TransportService, HttpTransport } from 'voltaire-effect'
const LoggingTransport = (url: string) =>
Layer.effect(
TransportService,
Effect.gen(function* () {
const base = yield* TransportService
return {
request: <T>(method: string, params?: unknown[]) =>
Effect.gen(function* () {
yield* Effect.log(`RPC: ${method}`, { params })
const start = Date.now()
const result = yield* base.request<T>(method, params)
yield* Effect.log(`RPC: ${method} completed in ${Date.now() - start}ms`)
return result
})
}
})
).pipe(Layer.provide(HttpTransport(url)))
Intercept All Requests
Copy
Ask AI
const program = Effect.gen(function* () {
const transport = yield* TransportService
// Wrap with logging
const loggedRequest = <T>(method: string, params?: unknown[]) =>
Effect.gen(function* () {
console.log(`→ ${method}`, params)
const result = yield* transport.request<T>(method, params)
console.log(`← ${method}`, result)
return result
})
return yield* loggedRequest('eth_blockNumber')
})
Timing
Measure Effect Duration
Copy
Ask AI
import { Effect, Duration } from 'effect'
const timed = program.pipe(
Effect.timed,
Effect.map(([duration, result]) => {
console.log(`Took ${Duration.toMillis(duration)}ms`)
return result
})
)
Add Timeout With Debug
Copy
Ask AI
program.pipe(
Effect.timeout(Duration.seconds(10)),
Effect.catchTag('TimeoutException', () => {
console.error('Operation timed out after 10s')
return Effect.fail(new Error('Timeout'))
})
)
Inspect Layer Composition
Check What’s Provided
Copy
Ask AI
import { Layer, Context } from 'effect'
import type { GetBalanceError } from 'voltaire-effect'
// TypeScript shows missing requirements
const program: Effect.Effect<bigint, GetBalanceError, ProviderService>
// Compose layers first, then provide once:
const ProviderLayer = Provider.pipe(Layer.provide(HttpTransport(url)))
const provided = program.pipe(Effect.provide(ProviderLayer))
// Effect.Effect<bigint, GetBalanceError, never>
// ↑ no more requirements
Debug Layer Construction
Copy
Ask AI
const MyLayer = Layer.effect(
MyService,
Effect.gen(function* () {
yield* Effect.log('Building MyService layer')
// ... construct service
return service
})
)
Common Issues
Missing Layer
Error: “Service not found: ProviderService” Fix: Compose and provide all required layers:Copy
Ask AI
// Compose layers first
const ProviderLayer = Provider.pipe(Layer.provide(HttpTransport(url)))
// Then provide once
program.pipe(Effect.provide(ProviderLayer))
Wrong Layer Order
Layers that depend on other layers must be composed with their dependencies:Copy
Ask AI
// ✓ Correct - compose layers with dependencies
const TransportLayer = HttpTransport(url)
const ProviderLayer = Provider.pipe(Layer.provide(TransportLayer))
const SignerLayer = Signer.Live.pipe(
Layer.provide(LocalAccount(key)),
Layer.provide(ProviderLayer)
)
// ✗ Wrong - Provider doesn't need Signer
Provider.pipe(
Layer.provide(Signer.Live), // Incorrect direction
Layer.provide(HttpTransport(url))
)
Forgot to Yield
Copy
Ask AI
import { getBlockNumber } from 'voltaire-effect'
// ✗ Returns Effect, doesn't execute
Effect.gen(function* () {
getBlockNumber() // Missing yield*!
})
// ✓ Correct
Effect.gen(function* () {
return yield* getBlockNumber()
})
Mixed Async Styles
Copy
Ask AI
// ✗ Don't mix await and yield*
Effect.gen(function* () {
const result = await somePromise() // Wrong!
})
// ✓ Wrap promises
Effect.gen(function* () {
const result = yield* Effect.promise(() => somePromise())
})
Testing Tips
Snapshot Error Output
Copy
Ask AI
import { describe, it, expect } from 'vitest'
import { Effect, Exit } from 'effect'
it('produces expected error', async () => {
const exit = await Effect.runPromiseExit(failingProgram)
if (Exit.isFailure(exit)) {
expect(exit.cause._tag).toBe('Fail')
expect(exit.cause.error._tag).toBe('ParseError')
}
})
Mock With Specific Errors
Copy
Ask AI
import { ProviderResponseError, TransportError } from 'voltaire-effect'
const FailingProvider = Layer.succeed(ProviderService, {
request: (method: string, _params?: unknown[]) => {
switch (method) {
case 'eth_blockNumber':
return Effect.fail(
new ProviderResponseError({ method: 'eth_blockNumber' }, 'Invalid response')
)
case 'eth_getBalance':
return Effect.fail(
new TransportError({ code: 429, message: 'Rate limited' })
)
default:
return Effect.succeed(null)
}
}
})
const exit = await Effect.runPromiseExit(
program.pipe(Effect.provide(FailingProvider))
)
See Also
- Error Handling — Typed error patterns
- Testing — Mock services for tests
- Effect Debugging — Official Effect debugging guide
- Effect Logging — Structured logging in Effect

