or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
golangpkg:golang/github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue/v2@v2.0.1

docs

error-handling.mdindex.mdqueue-client.mdsas.mdservice-client.md
tile.json

tessl/golang-github-com-azure-azure-sdk-for-go-sdk-storage-azqueue-v2

tessl install tessl/golang-github-com-azure-azure-sdk-for-go-sdk-storage-azqueue-v2@2.0.1

Azure Queue Storage SDK for Go provides comprehensive client library for interacting with Azure's cloud-based message queue service including queue management, message operations, and multiple authentication methods.

error-handling.mddocs/

Error Handling

The queueerror package (github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue/v2/queueerror) provides comprehensive error handling capabilities including predefined error codes and helper functions for identifying and handling specific Azure Queue Storage errors.

Capabilities

Error Code Checking

Check if an error matches specific error codes.

// Check if error matches any of the specified error codes
func HasCode(err error, codes ...Code) bool

Usage:

import (
    "context"
    "github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue/v2"
    "github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue/v2/queueerror"
)

// Attempt to delete queue
_, err := serviceClient.DeleteQueue(context.TODO(), "myqueue", nil)

if queueerror.HasCode(err, queueerror.QueueBeingDeleted, queueerror.QueueNotFound) {
    // Queue is being deleted or already doesn't exist - safe to ignore
    fmt.Println("Queue already deleted or being deleted")
} else if err != nil {
    // Handle other errors
    return fmt.Errorf("failed to delete queue: %w", err)
}

Error Code Type

// Error code returned by Azure Queue Storage service
type Code = generated.StorageErrorCode

All Azure Queue Storage service errors are returned as *azcore.ResponseError with an ErrorCode field containing one of the predefined error codes.

Error Variables

var (
    // Error returned when attempting to create SAS URL without SharedKeyCredential
    MissingSharedKeyCredential = errors.New("SAS can only be signed with a SharedKeyCredential")
)

Usage:

import "errors"

sasURL, err := queueClient.GetSASURL(permissions, expiry, nil)
if errors.Is(err, queueerror.MissingSharedKeyCredential) {
    // Client was not created with SharedKeyCredential
    fmt.Println("Cannot generate SAS - SharedKeyCredential required")
}

Error Code Constants

Account Errors

const (
    AccountAlreadyExists Code = "AccountAlreadyExists"
    AccountBeingCreated  Code = "AccountBeingCreated"
    AccountIsDisabled    Code = "AccountIsDisabled"
)

Usage:

if queueerror.HasCode(err, queueerror.AccountIsDisabled) {
    fmt.Println("Storage account is disabled")
}

Authentication and Authorization Errors

const (
    AuthenticationFailed              Code = "AuthenticationFailed"
    AuthorizationFailure              Code = "AuthorizationFailure"
    AuthorizationPermissionMismatch   Code = "AuthorizationPermissionMismatch"
    AuthorizationProtocolMismatch     Code = "AuthorizationProtocolMismatch"
    AuthorizationResourceTypeMismatch Code = "AuthorizationResourceTypeMismatch"
    AuthorizationServiceMismatch      Code = "AuthorizationServiceMismatch"
    AuthorizationSourceIPMismatch     Code = "AuthorizationSourceIPMismatch"
    InsufficientAccountPermissions    Code = "InsufficientAccountPermissions"
    InvalidAuthenticationInfo         Code = "InvalidAuthenticationInfo"
)

Usage:

if queueerror.HasCode(err,
    queueerror.AuthenticationFailed,
    queueerror.InvalidAuthenticationInfo) {
    fmt.Println("Authentication failed - check credentials")
}

if queueerror.HasCode(err,
    queueerror.AuthorizationPermissionMismatch,
    queueerror.InsufficientAccountPermissions) {
    fmt.Println("Insufficient permissions for operation")
}

if queueerror.HasCode(err, queueerror.AuthorizationSourceIPMismatch) {
    fmt.Println("Request from unauthorized IP address")
}

Queue Errors

const (
    QueueAlreadyExists Code = "QueueAlreadyExists"
    QueueBeingDeleted  Code = "QueueBeingDeleted"
    QueueDisabled      Code = "QueueDisabled"
    QueueNotEmpty      Code = "QueueNotEmpty"
    QueueNotFound      Code = "QueueNotFound"
)

Usage:

// Handle queue creation
_, err := queueClient.Create(context.TODO(), nil)
if queueerror.HasCode(err, queueerror.QueueAlreadyExists) {
    fmt.Println("Queue already exists - continuing")
} else if err != nil {
    return err
}

// Handle queue deletion
_, err = queueClient.Delete(context.TODO(), nil)
if queueerror.HasCode(err, queueerror.QueueNotFound) {
    fmt.Println("Queue not found - already deleted")
} else if queueerror.HasCode(err, queueerror.QueueBeingDeleted) {
    fmt.Println("Queue is being deleted")
} else if err != nil {
    return err
}

Message Errors

const (
    MessageNotFound Code = "MessageNotFound"
    MessageTooLarge Code = "MessageTooLarge"
    PopReceiptMismatch Code = "PopReceiptMismatch"
)

Usage:

// Handle message deletion
_, err := queueClient.DeleteMessage(context.TODO(), messageID, popReceipt, nil)
if queueerror.HasCode(err, queueerror.MessageNotFound) {
    fmt.Println("Message already deleted or expired")
} else if queueerror.HasCode(err, queueerror.PopReceiptMismatch) {
    fmt.Println("PopReceipt is invalid - message may have been updated")
} else if err != nil {
    return err
}

// Handle message enqueue
_, err = queueClient.EnqueueMessage(context.TODO(), largeContent, nil)
if queueerror.HasCode(err, queueerror.MessageTooLarge) {
    fmt.Println("Message exceeds 64 KiB size limit")
}

Validation Errors

const (
    InvalidHTTPVerb           Code = "InvalidHttpVerb"
    InvalidHeaderValue        Code = "InvalidHeaderValue"
    InvalidInput              Code = "InvalidInput"
    InvalidMD5                Code = "InvalidMd5"
    InvalidMarker             Code = "InvalidMarker"
    InvalidMetadata           Code = "InvalidMetadata"
    InvalidQueryParameterValue Code = "InvalidQueryParameterValue"
    InvalidRange              Code = "InvalidRange"
    InvalidResourceName       Code = "InvalidResourceName"
    InvalidURI                Code = "InvalidUri"
    InvalidXMLDocument        Code = "InvalidXmlDocument"
    InvalidXMLNodeValue       Code = "InvalidXmlNodeValue"
    MD5Mismatch               Code = "Md5Mismatch"
)

Usage:

if queueerror.HasCode(err,
    queueerror.InvalidResourceName,
    queueerror.InvalidInput) {
    fmt.Println("Invalid queue name or parameters")
}

if queueerror.HasCode(err, queueerror.InvalidMetadata) {
    fmt.Println("Metadata contains invalid characters or exceeds size limit")
}

Metadata Errors

const (
    EmptyMetadataKey Code = "EmptyMetadataKey"
    MetadataTooLarge Code = "MetadataTooLarge"
)

Usage:

_, err := queueClient.SetMetadata(context.TODO(), &azqueue.SetMetadataOptions{
    Metadata: metadata,
})
if queueerror.HasCode(err, queueerror.EmptyMetadataKey) {
    fmt.Println("Metadata key cannot be empty")
} else if queueerror.HasCode(err, queueerror.MetadataTooLarge) {
    fmt.Println("Metadata exceeds maximum size (8 KB)")
}

Request Errors

const (
    MissingContentLengthHeader   Code = "MissingContentLengthHeader"
    MissingRequiredHeader        Code = "MissingRequiredHeader"
    MissingRequiredQueryParameter Code = "MissingRequiredQueryParameter"
    MissingRequiredXMLNode       Code = "MissingRequiredXmlNode"
    RequestBodyTooLarge          Code = "RequestBodyTooLarge"
    RequestURLFailedToParse      Code = "RequestUrlFailedToParse"
)

Resource Errors

const (
    ResourceAlreadyExists Code = "ResourceAlreadyExists"
    ResourceNotFound      Code = "ResourceNotFound"
    ResourceTypeMismatch  Code = "ResourceTypeMismatch"
)

Usage:

if queueerror.HasCode(err, queueerror.ResourceNotFound) {
    fmt.Println("Resource not found")
}

Condition Errors

const (
    ConditionHeadersNotSupported         Code = "ConditionHeadersNotSupported"
    ConditionNotMet                      Code = "ConditionNotMet"
    MultipleConditionHeadersNotSupported Code = "MultipleConditionHeadersNotSupported"
)

General Service Errors

const (
    FeatureVersionMismatch        Code = "FeatureVersionMismatch"
    InternalError                 Code = "InternalError"
    OperationTimedOut             Code = "OperationTimedOut"
    OutOfRangeInput               Code = "OutOfRangeInput"
    OutOfRangeQueryParameterValue Code = "OutOfRangeQueryParameterValue"
    ServerBusy                    Code = "ServerBusy"
    UnsupportedHTTPVerb           Code = "UnsupportedHttpVerb"
    UnsupportedHeader             Code = "UnsupportedHeader"
    UnsupportedQueryParameter     Code = "UnsupportedQueryParameter"
    UnsupportedXMLNode            Code = "UnsupportedXmlNode"
)

Usage:

if queueerror.HasCode(err, queueerror.ServerBusy) {
    // Implement exponential backoff retry
    fmt.Println("Server busy - retrying after delay")
}

if queueerror.HasCode(err, queueerror.OperationTimedOut) {
    fmt.Println("Operation timed out - retry operation")
}

if queueerror.HasCode(err, queueerror.InternalError) {
    fmt.Println("Internal server error - retry operation")
}

Error Handling Patterns

Pattern 1: Idempotent Operations

import (
    "context"
    "github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue/v2"
    "github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue/v2/queueerror"
)

func ensureQueueExists(client *azqueue.QueueClient) error {
    _, err := client.Create(context.TODO(), nil)
    if err == nil {
        return nil // Successfully created
    }

    if queueerror.HasCode(err, queueerror.QueueAlreadyExists) {
        return nil // Already exists - success
    }

    return err // Other error
}

Pattern 2: Safe Deletion

func safeDeleteQueue(client *azqueue.QueueClient) error {
    _, err := client.Delete(context.TODO(), nil)
    if err == nil {
        return nil // Successfully deleted
    }

    // Ignore if queue doesn't exist or is already being deleted
    if queueerror.HasCode(err,
        queueerror.QueueNotFound,
        queueerror.QueueBeingDeleted) {
        return nil
    }

    return err // Other error
}

Pattern 3: Message Processing with Error Handling

func processMessage(client *azqueue.QueueClient) error {
    // Dequeue message
    resp, err := client.DequeueMessage(context.TODO(), nil)
    if err != nil {
        return fmt.Errorf("dequeue failed: %w", err)
    }

    if len(resp.Messages) == 0 {
        return nil // No messages available
    }

    msg := resp.Messages[0]

    // Process message...
    processErr := processMessageContent(*msg.MessageText)
    if processErr != nil {
        // Processing failed - message will become visible again
        return processErr
    }

    // Delete message after successful processing
    _, err = client.DeleteMessage(
        context.TODO(),
        *msg.MessageID,
        *msg.PopReceipt,
        nil,
    )

    if queueerror.HasCode(err, queueerror.MessageNotFound) {
        // Message already deleted or expired - not an error
        return nil
    }

    if queueerror.HasCode(err, queueerror.PopReceiptMismatch) {
        // PopReceipt invalid - message was updated by another process
        return fmt.Errorf("message was modified by another process")
    }

    return err
}

func processMessageContent(content string) error {
    // Application-specific processing logic
    return nil
}

Pattern 4: Retry with Exponential Backoff

import (
    "time"
    "math"
)

func enqueueWithRetry(client *azqueue.QueueClient, content string, maxRetries int) error {
    var lastErr error

    for attempt := 0; attempt < maxRetries; attempt++ {
        _, err := client.EnqueueMessage(context.TODO(), content, nil)
        if err == nil {
            return nil // Success
        }

        // Check if error is retryable
        if queueerror.HasCode(err,
            queueerror.ServerBusy,
            queueerror.OperationTimedOut,
            queueerror.InternalError) {

            lastErr = err

            // Exponential backoff: 100ms, 200ms, 400ms, 800ms, ...
            backoff := time.Duration(math.Pow(2, float64(attempt))) * 100 * time.Millisecond
            time.Sleep(backoff)
            continue
        }

        // Non-retryable error
        return err
    }

    return fmt.Errorf("max retries exceeded: %w", lastErr)
}

Pattern 5: Detailed Error Logging

import (
    "fmt"
    "github.com/Azure/azure-sdk-for-go/sdk/azcore"
)

func handleError(err error) {
    if err == nil {
        return
    }

    // Check if it's an Azure service error
    var respErr *azcore.ResponseError
    if errors.As(err, &respErr) {
        fmt.Printf("Azure error occurred:\n")
        fmt.Printf("  Error Code: %s\n", respErr.ErrorCode)
        fmt.Printf("  Status Code: %d\n", respErr.StatusCode)
        fmt.Printf("  Message: %s\n", respErr.Error())

        // Check specific error codes
        if queueerror.HasCode(err, queueerror.AuthenticationFailed) {
            fmt.Println("  -> Check your credentials")
        } else if queueerror.HasCode(err, queueerror.QueueNotFound) {
            fmt.Println("  -> Queue does not exist")
        } else if queueerror.HasCode(err, queueerror.MessageTooLarge) {
            fmt.Println("  -> Message exceeds 64 KiB limit")
        }
    } else {
        // Not an Azure service error
        fmt.Printf("Error: %v\n", err)
    }
}

Best Practices

  1. Use HasCode for Error Matching: Always use queueerror.HasCode() instead of string comparison for error codes
  2. Handle Transient Errors: Implement retry logic for transient errors (ServerBusy, OperationTimedOut, InternalError)
  3. Idempotent Operations: Design operations to be idempotent and ignore appropriate error codes (e.g., QueueAlreadyExists when creating)
  4. Log Errors Properly: Extract and log error codes, status codes, and messages for debugging
  5. Validate Input Early: Check input validity before making API calls to avoid validation errors
  6. PopReceipt Management: Always use the latest PopReceipt after UpdateMessage operations
  7. Message Size: Validate message size (≤64 KiB) before enqueuing to avoid MessageTooLarge errors
  8. Metadata Validation: Ensure metadata keys are non-empty and total size ≤8 KB

Common Error Scenarios

Scenario 1: Queue Creation Race Condition

// Multiple processes trying to create the same queue
_, err := queueClient.Create(context.TODO(), nil)
if err != nil && !queueerror.HasCode(err, queueerror.QueueAlreadyExists) {
    return err
}
// Continue - queue exists

Scenario 2: Message Already Processed

// Another worker may have already processed the message
_, err := queueClient.DeleteMessage(context.TODO(), messageID, popReceipt, nil)
if err != nil && !queueerror.HasCode(err, queueerror.MessageNotFound) {
    return err
}
// Continue - message deleted

Scenario 3: SAS Token Issues

_, err := queueClient.EnqueueMessage(context.TODO(), content, nil)
if queueerror.HasCode(err,
    queueerror.AuthenticationFailed,
    queueerror.AuthorizationFailure) {
    return errors.New("SAS token is invalid or expired")
}

Scenario 4: IP Restriction Violation

if queueerror.HasCode(err, queueerror.AuthorizationSourceIPMismatch) {
    return errors.New("request originated from unauthorized IP address")
}