The Togo MQ SDK provides detailed error information to help you handle failures gracefully and debug issues effectively. All SDK operations return errors that can be type-asserted to get more detailed information.
The SDK uses a custom error type that provides structured error information:
type TogoMQError struct {
Code string // Error code for programmatic handling
Message string // Human-readable error message
Err error // Original underlying error (if any)
}
Check if an error is a TogoMQ error:
resp, err := client.PubBatch(ctx, messages)
if err != nil {
if togomqErr, ok := err.(*togomq.TogoMQError); ok {
// It's a TogoMQ error
log.Printf("Code: %s, Message: %s\n", togomqErr.Code, togomqErr.Message)
} else {
// It's a different type of error
log.Printf("Unexpected error: %v\n", err)
}
}
The SDK defines the following error codes:
| Error Code | Description | Common Causes |
|---|---|---|
ErrCodeConnection |
Connection or network errors | Network issues, firewall, DNS problems |
ErrCodeAuth |
Authentication failures | Invalid token, expired token, revoked token |
ErrCodeValidation |
Invalid input or configuration | Missing topic, invalid config, malformed data |
ErrCodePublish |
Publishing errors | Server-side publish failures |
ErrCodeSubscribe |
Subscription errors | Server-side subscription failures |
ErrCodeStream |
General streaming errors | Stream interrupted, connection lost |
ErrCodeConfiguration |
Configuration errors | Invalid host, port, or other config |
resp, err := client.PubBatch(ctx, messages)
if err != nil {
log.Printf("Failed to publish: %v\n", err)
return
}
resp, err := client.PubBatch(ctx, messages)
if err != nil {
// Check error type
if togomqErr, ok := err.(*togomq.TogoMQError); ok {
switch togomqErr.Code {
case togomq.ErrCodeAuth:
log.Println("Authentication failed - check your token")
// Maybe refresh token or alert admin
case togomq.ErrCodeConnection:
log.Println("Connection error - will retry")
// Implement retry logic
case togomq.ErrCodeValidation:
log.Printf("Validation error: %s\n", togomqErr.Message)
// Fix the invalid input
case togomq.ErrCodePublish:
log.Printf("Publish failed: %s\n", togomqErr.Message)
// Queue for retry or alert monitoring
default:
log.Printf("Error (%s): %s\n", togomqErr.Code, togomqErr.Message)
}
} else {
log.Printf("Unexpected error: %v\n", err)
}
return
}
msgChan, errChan, err := client.Sub(ctx, opts)
if err != nil {
log.Fatalf("Failed to subscribe: %v", err)
}
for {
select {
case msg := <-msgChan:
if msg == nil {
return
}
if err := processMessage(msg); err != nil {
log.Printf("Failed to process message %s: %v\n", msg.UUID, err)
// Decide whether to continue or stop
}
case err := <-errChan:
if togomqErr, ok := err.(*togomq.TogoMQError); ok {
log.Printf("Subscription error (%s): %s\n", togomqErr.Code, togomqErr.Message)
// Handle specific errors
if togomqErr.Code == togomq.ErrCodeAuth {
log.Fatal("Authentication failed - stopping")
}
} else {
log.Printf("Unexpected subscription error: %v\n", err)
}
return
}
}
Cause: Invalid, expired, or revoked token
resp, err := client.PubBatch(ctx, messages)
if err != nil {
if togomqErr, ok := err.(*togomq.TogoMQError); ok {
if togomqErr.Code == togomq.ErrCodeAuth {
log.Println("Authentication failed!")
log.Println("Possible causes:")
log.Println("- Token is invalid")
log.Println("- Token has been revoked")
log.Println("- Token has expired")
log.Println("Please check your token in the dashboard")
}
}
}
Solution:
Cause: Network issues, firewall, or DNS problems
resp, err := client.PubBatch(ctx, messages)
if err != nil {
if togomqErr, ok := err.(*togomq.TogoMQError); ok {
if togomqErr.Code == togomq.ErrCodeConnection {
log.Println("Connection error - implementing retry logic")
// Exponential backoff retry
for attempt := 1; attempt <= 3; attempt++ {
time.Sleep(time.Duration(attempt) * time.Second)
resp, err = client.PubBatch(ctx, messages)
if err == nil {
log.Println("Retry successful!")
break
}
log.Printf("Retry %d failed\n", attempt)
}
}
}
}
Solution:
Cause: Invalid input data (e.g., missing topic)
// This will cause a validation error - topic is required!
msg := togomq.NewMessage("", []byte("data"))
resp, err := client.PubBatch(ctx, []*togomq.Message{msg})
if err != nil {
if togomqErr, ok := err.(*togomq.TogoMQError); ok {
if togomqErr.Code == togomq.ErrCodeValidation {
log.Printf("Validation error: %s\n", togomqErr.Message)
// Fix: Always provide a topic
msg.Topic = "events"
}
}
}
Solution:
Cause: Operation took longer than context timeout
// Set a very short timeout (for demonstration)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)
defer cancel()
resp, err := client.PubBatch(ctx, messages)
if err != nil {
if err == context.DeadlineExceeded {
log.Println("Operation timed out!")
log.Println("Consider increasing timeout or checking network")
}
}
Solution:
Never ignore errors:
// Good ✅
resp, err := client.PubBatch(ctx, messages)
if err != nil {
log.Printf("Error: %v\n", err)
return
}
// Bad ❌
client.PubBatch(ctx, messages)
Get detailed error information:
if togomqErr, ok := err.(*togomq.TogoMQError); ok {
// Handle specific error codes
switch togomqErr.Code {
case togomq.ErrCodeAuth:
// Handle auth errors
case togomq.ErrCodeConnection:
// Handle connection errors
}
}
For transient errors (connection issues):
func publishWithRetry(client *togomq.Client, messages []*togomq.Message, maxRetries int) error {
var err error
for attempt := 0; attempt < maxRetries; attempt++ {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
_, err = client.PubBatch(ctx, messages)
cancel()
if err == nil {
return nil // Success
}
// Check if we should retry
if togomqErr, ok := err.(*togomq.TogoMQError); ok {
if togomqErr.Code == togomq.ErrCodeAuth ||
togomqErr.Code == togomq.ErrCodeValidation {
// Don't retry auth or validation errors
return err
}
}
// Exponential backoff
if attempt < maxRetries-1 {
backoff := time.Duration(1<<uint(attempt)) * time.Second
log.Printf("Retry %d after %v\n", attempt+1, backoff)
time.Sleep(backoff)
}
}
return err
}
Include error codes and messages in logs:
if togomqErr, ok := err.(*togomq.TogoMQError); ok {
log.Printf("TogoMQ Error - Code: %s, Message: %s, Underlying: %v\n",
togomqErr.Code,
togomqErr.Message,
togomqErr.Err,
)
}
Track error rates for alerting:
var (
totalRequests int64
failedRequests int64
)
func publish(client *togomq.Client, messages []*togomq.Message) {
atomic.AddInt64(&totalRequests, 1)
resp, err := client.PubBatch(ctx, messages)
if err != nil {
atomic.AddInt64(&failedRequests, 1)
errorRate := float64(failedRequests) / float64(totalRequests)
if errorRate > 0.05 { // 5% error rate
log.Printf("WARNING: High error rate: %.2f%%\n", errorRate*100)
// Alert monitoring system
}
}
}
Always set reasonable timeouts:
// Set timeout based on operation type
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
resp, err := client.PubBatch(ctx, messages)
if err != nil {
if err == context.DeadlineExceeded {
log.Println("Operation timed out")
}
}
package main
import (
"context"
"log"
"os"
"time"
"github.com/TogoMQ/togomq-sdk-go"
)
func main() {
config := togomq.NewConfig(
togomq.WithToken(os.Getenv("TOGOMQ_TOKEN")),
togomq.WithLogLevel("info"),
)
client, err := togomq.NewClient(config)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer client.Close()
messages := []*togomq.Message{
togomq.NewMessage("events", []byte("test-message")),
}
// Publish with comprehensive error handling
if err := publishWithErrorHandling(client, messages); err != nil {
log.Fatalf("Final error: %v", err)
}
}
func publishWithErrorHandling(client *togomq.Client, messages []*togomq.Message) error {
maxRetries := 3
for attempt := 0; attempt < maxRetries; attempt++ {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
resp, err := client.PubBatch(ctx, messages)
cancel()
if err == nil {
log.Printf("Success! Published %d messages\n", resp.MessagesReceived)
return nil
}
// Handle context errors
if err == context.DeadlineExceeded {
log.Printf("Attempt %d: Timeout\n", attempt+1)
continue
}
// Handle TogoMQ errors
if togomqErr, ok := err.(*togomq.TogoMQError); ok {
log.Printf("Attempt %d: Error code %s - %s\n",
attempt+1, togomqErr.Code, togomqErr.Message)
switch togomqErr.Code {
case togomq.ErrCodeAuth:
log.Println("Authentication failed - not retrying")
return err
case togomq.ErrCodeValidation:
log.Println("Validation error - not retrying")
return err
case togomq.ErrCodeConnection, togomq.ErrCodePublish:
if attempt < maxRetries-1 {
backoff := time.Duration(1<<uint(attempt)) * time.Second
log.Printf("Retrying after %v\n", backoff)
time.Sleep(backoff)
continue
}
default:
log.Printf("Unknown error code: %s\n", togomqErr.Code)
}
}
// If we get here and it's the last attempt, return the error
if attempt == maxRetries-1 {
return err
}
}
return nil
}
{success} Next: Check out the FAQ for answers to common questions about Togo MQ.