Error Handling¶
Voltricx uses a clear exception hierarchy rooted at VoltricxException. All errors can be caught with a single broad except voltricx.VoltricxException block, or handled individually for fine-grained control.
Exception Hierarchy¶
VoltricxException (base)
├── NodeException # Generic node/REST errors
├── LavalinkException # Lavalink API error responses
├── LavalinkLoadException # Track loading failures
├── InvalidNodeException # Node not found / no nodes available
├── ChannelTimeoutException # Voice connect timeout
├── InvalidChannelStateException # Invalid channel on connect
└── QueueEmpty # Tried to get() from empty queue
VoltricxException¶
The base exception. Catch this to handle all Voltricx errors at once.
try:
await player.play(track)
except voltricx.VoltricxException as e:
await ctx.send(f"An error occurred: {e}")
NodeException¶
Raised on generic REST failures or when the session ID is missing.
try:
await node.fetch_info()
except voltricx.NodeException as e:
print(f"Node error (HTTP {e.status}): {e}")
| Attribute | Type | Description |
|---|---|---|
e.status | int \| None | HTTP status code if applicable |
LavalinkException¶
Raised when the Lavalink server returns a structured error response (HTTP 4xx/5xx).
try:
data = await node.load_tracks("invalid://query")
except voltricx.LavalinkException as e:
print(f"Lavalink error {e.status}: {e.error}")
print(f"Path: {e.path}")
| Attribute | Type | Description |
|---|---|---|
e.timestamp | int | Unix timestamp of the error |
e.status | int | HTTP status code |
e.error | str | Error type (e.g. "Not Found") |
e.trace | str \| None | Stack trace from Lavalink (debug mode) |
e.path | str | API path that failed |
LavalinkLoadException¶
Raised when Lavalink's loadType is "error" (track could not be loaded).
try:
results = await voltricx.Pool.fetch_tracks("https://bad.url/track")
except voltricx.LavalinkLoadException as e:
print(f"Load failed: {e.message} (Severity: {e.severity})")
| Attribute | Type | Description |
|---|---|---|
e.message | str | Human-readable error message |
e.severity | str | "common", "suspicious", or "fault" |
e.cause | str | Root cause string |
InvalidNodeException¶
Raised when requesting a node that doesn't exist, or when the Pool has no connected nodes.
try:
node = voltricx.Pool.get_node("NonExistent")
except voltricx.InvalidNodeException as e:
print(f"Node error: {e}")
ChannelTimeoutException¶
Raised when connecting to a voice channel times out (default: 10 seconds).
try:
player = await channel.connect(cls=voltricx.Player)
except voltricx.ChannelTimeoutException:
await ctx.send("❌ Connection timed out. Is the bot in a restricted channel?")
InvalidChannelStateException¶
Raised when the player tries to connect without a valid channel set.
try:
await player.connect(timeout=10.0, reconnect=True)
except voltricx.InvalidChannelStateException as e:
print(f"Channel state error: {e}")
QueueEmpty¶
Raised when calling queue.get() on an empty queue.
from voltricx import QueueEmpty
try:
track = player.queue.get()
except QueueEmpty:
await ctx.send("The queue is empty!")
Global Error Handler¶
A recommended pattern using discord.py's command error handler:
@bot.event
async def on_command_error(ctx: commands.Context, error: Exception):
# Unwrap CommandInvokeError
error = getattr(error, "original", error)
if isinstance(error, voltricx.ChannelTimeoutException):
await ctx.send("❌ Voice channel connection timed out.")
elif isinstance(error, voltricx.InvalidNodeException):
await ctx.send("❌ No Lavalink nodes are available right now.")
elif isinstance(error, voltricx.LavalinkLoadException):
await ctx.send(f"❌ Could not load track: `{error.message}`")
elif isinstance(error, voltricx.QueueEmpty):
await ctx.send("❌ The queue is empty.")
elif isinstance(error, voltricx.VoltricxException):
await ctx.send(f"❌ Music error: {error}")
elif isinstance(error, commands.CheckFailure):
await ctx.send(f"❌ {error}")
else:
raise error