Skip to content

Error Handling

Best practices and patterns for error handling in Movian plugins.

Overview

Proper error handling ensures: - Graceful degradation - User-friendly error messages - Debugging information - Application stability - Data integrity

Error Types

JavaScript Errors

// Syntax errors
try {
    eval("invalid javascript{");
} catch (e) {
    console.error("Syntax error:", e);
}

// Runtime errors
try {
    var obj = null;
    obj.property;  // TypeError
} catch (e) {
    console.error("Runtime error:", e);
}

// Type errors
try {
    var num = "not a number";
    num.toFixed(2);  // TypeError
} catch (e) {
    console.error("Type error:", e);
}

Network Errors

http.request(url, function(err, response) {
    if (err) {
        if (err.code === 'ETIMEDOUT') {
            page.error("Request timed out");
        } else if (err.code === 'ENOTFOUND') {
            page.error("Server not found");
        } else {
            page.error("Network error: " + err.message);
        }
        return;
    }
    // Process response
});

API Errors

try {
    var data = JSON.parse(response.toString());

    if (data.error) {
        throw new Error("API error: " + data.error.message);
    }

    if (!data.results) {
        throw new Error("Invalid API response");
    }

    processData(data.results);
} catch (e) {
    console.error("API error:", e);
    page.error("Failed to process data");
}

Error Handling Patterns

Try-Catch Pattern

function safeOperation() {
    try {
        // Risky operation
        var data = JSON.parse(response);
        return processData(data);
    } catch (e) {
        console.error("Operation failed:", e);
        return null;
    }
}

Error-First Callbacks

function asyncOperation(callback) {
    http.request(url, function(err, response) {
        if (err) {
            callback(err, null);
            return;
        }

        try {
            var data = JSON.parse(response.toString());
            callback(null, data);
        } catch (e) {
            callback(e, null);
        }
    });
}

// Usage
asyncOperation(function(err, data) {
    if (err) {
        console.error("Error:", err);
        return;
    }
    // Use data
});

Guard Clauses

function processUser(user) {
    if (!user) {
        console.error("User is null");
        return;
    }

    if (!user.id) {
        console.error("User has no ID");
        return;
    }

    if (!user.name) {
        console.error("User has no name");
        return;
    }

    // Process valid user
    console.log("Processing user:", user.name);
}

Validation Functions

function validateEmail(email) {
    if (!email) {
        throw new Error("Email is required");
    }

    if (typeof email !== 'string') {
        throw new Error("Email must be a string");
    }

    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
        throw new Error("Invalid email format");
    }

    return email.toLowerCase();
}

try {
    var email = validateEmail(userInput);
    // Use validated email
} catch (e) {
    page.error(e.message);
}

User-Facing Errors

Page Errors

// Display error to user
page.error("Failed to load content");

// Error with details
page.error("Network error: " + err.message);

// Temporary error (can retry)
page.error("Temporary error, please try again");

Loading States

page.loading = true;

http.request(url, function(err, response) {
    page.loading = false;

    if (err) {
        page.error("Failed to load: " + err.message);
        return;
    }

    // Display content
});

Graceful Degradation

function loadContent(page) {
    http.request(primaryUrl, function(err, response) {
        if (err) {
            console.log("Primary source failed, trying fallback");

            http.request(fallbackUrl, function(err2, response2) {
                if (err2) {
                    page.error("All sources failed");
                    return;
                }

                displayContent(page, response2);
            });
            return;
        }

        displayContent(page, response);
    });
}

Logging and Debugging

Console Logging

// Different log levels
console.log("Info message");
console.warn("Warning message");
console.error("Error message");

// Structured logging
console.log("Operation:", operation, "Status:", status);

// Object inspection
console.log("Data:", JSON.stringify(data, null, 2));

Error Context

function processData(data, context) {
    try {
        // Process data
        return transform(data);
    } catch (e) {
        console.error("Error in processData:", {
            error: e.message,
            context: context,
            data: data
        });
        throw e;
    }
}

Stack Traces

try {
    riskyOperation();
} catch (e) {
    console.error("Error:", e.message);
    console.error("Stack:", e.stack);
}

Retry Logic

Simple Retry

function retryRequest(url, maxRetries, callback) {
    var attempts = 0;

    function attempt() {
        attempts++;

        http.request(url, function(err, response) {
            if (err && attempts < maxRetries) {
                console.log("Retry attempt", attempts);
                setTimeout(attempt, 1000 * attempts);  // Exponential backoff
                return;
            }

            callback(err, response);
        });
    }

    attempt();
}

// Usage
retryRequest(url, 3, function(err, response) {
    if (err) {
        page.error("Failed after 3 retries");
        return;
    }
    // Process response
});

Exponential Backoff

function exponentialBackoff(func, maxRetries) {
    var attempts = 0;

    function retry() {
        attempts++;

        func(function(err, result) {
            if (err && attempts < maxRetries) {
                var delay = Math.pow(2, attempts) * 1000;  // 2s, 4s, 8s, ...
                console.log("Retrying in", delay, "ms");
                setTimeout(retry, delay);
                return;
            }

            if (err) {
                console.error("Failed after", attempts, "attempts");
            }
        });
    }

    retry();
}

Error Recovery

State Recovery

var lastGoodState = null;

function updateState(newState) {
    try {
        validateState(newState);
        lastGoodState = newState;
        applyState(newState);
    } catch (e) {
        console.error("Invalid state, reverting:", e);
        if (lastGoodState) {
            applyState(lastGoodState);
        }
    }
}

Transaction Rollback

function atomicUpdate(db, operations) {
    try {
        db.begin();

        operations.forEach(function(op) {
            op(db);
        });

        db.commit();
        return true;
    } catch (e) {
        console.error("Transaction failed:", e);
        db.rollback();
        return false;
    }
}

Best Practices

Do's ✅

  • Always handle errors - Never ignore error conditions
  • Provide context - Include relevant information in error messages
  • Log errors - Use console.error for debugging
  • Validate input - Check data before processing
  • Use try-catch - Wrap risky operations
  • Fail gracefully - Provide fallbacks when possible
  • Clean up resources - Release resources in finally blocks
  • Test error paths - Verify error handling works

Don'ts ❌

  • Don't swallow errors - Always log or handle them
  • Don't expose internals - Show user-friendly messages
  • Don't crash - Catch and handle exceptions
  • Don't retry forever - Set retry limits
  • Don't ignore warnings - They often indicate problems
  • Don't use errors for control flow - Use proper conditionals
  • Don't leak sensitive data - Sanitize error messages
  • Don't block on errors - Allow application to continue

Error Reporting

User Feedback

function handleError(err, userMessage) {
    // Log technical details
    console.error("Error:", err.message);
    console.error("Stack:", err.stack);

    // Show user-friendly message
    page.error(userMessage || "An error occurred");
}

Error Aggregation

var errorLog = [];

function logError(error, context) {
    errorLog.push({
        error: error.message,
        context: context,
        timestamp: Date.now()
    });

    // Keep only recent errors
    if (errorLog.length > 100) {
        errorLog.shift();
    }
}

function getErrorReport() {
    return JSON.stringify(errorLog, null, 2);
}

See Also