Skip to main content

Module Integration

Learn how to integrate your Foundry VTT module with Errors and Echoes for enhanced error reporting.

Quick Start

The simplest integration uses the hook-based registration system (recommended for v0.2.0+):

// Hook-based registration (recommended)
Hooks.on('errorsAndEchoesReady', (api) => {
api.register({
moduleId: 'your-module-id',

// Optional: Provide context for debugging
contextProvider: () => ({
gameSystem: game.system.id,
activeFeatures: yourModule.getActiveFeatures(),
userConfiguration: yourModule.getRelevantSettings(),
}),

// Optional: Filter errors to reduce noise
errorFilter: error => {
// Return true to filter OUT (not report)
// Return false to report the error
return !error.stack.includes('your-module-id');
},
});
});

Registration API

Basic Registration

At minimum, provide your module ID:

Hooks.on('errorsAndEchoesReady', (api) => {
api.register({
moduleId: 'my-awesome-module'
});
});

Full Registration Options

interface ModuleRegistrationConfig {
moduleId: string; // Your module's ID (required)
contextProvider?: () => Record<string, any>; // Optional context provider
errorFilter?: (error: Error) => boolean; // Optional error filter
endpoint?: EndpointConfig; // Optional custom endpoint
}

Context Provider

Provide additional debugging context when errors occur:

const contextProvider = () => ({
// Version information
moduleVersion: game.modules.get('my-module')?.version,
foundryVersion: game.version,

// Current state
activeScene: canvas.scene?.name,
selectedTokens: canvas.tokens?.controlled?.length || 0,

// Module-specific state
customSetting: game.settings.get('my-module', 'important-setting'),
featureEnabled: MyModule.isFeatureEnabled(),

// User context (be careful about privacy)
isGM: game.user.isGM,
// DON'T include: usernames, IP addresses, email addresses, etc.
});

Context Best Practices

DO Include:

  • Module version and configuration
  • Current game state (scene, tokens, etc.)
  • Feature flags and settings
  • Error context (what operation was happening)
  • System information (game system, Foundry version)

DON'T Include:

  • User names or identifiers
  • IP addresses or network information
  • Personal data or private information
  • Sensitive configuration (API keys, passwords)
  • Large data structures (limit to essential debugging info)

Error Filtering

Filter which errors to report to reduce noise:

const errorFilter = error => {
// Don't report errors we expect or can't fix
if (error.message.includes('Network request failed')) return true;
if (error.message.includes('Permission denied')) return true;

// Only report errors from our module
if (error.stack && !error.stack.includes('/modules/my-module/')) return true;

// Report this error (return false)
return false;
};

Filter Return Values

  • Return true: Filter OUT the error (don't report it)
  • Return false: Report the error
  • Return undefined: Report the error (default behavior)

Manual Error Reporting

Report specific errors with additional context:

try {
// Your code that might error
riskyOperation();
} catch (error) {
// Report to error monitoring
if (window.ErrorsAndEchoesAPI?.reportError) {
window.ErrorsAndEchoesAPI.reportError(error, {
operation: 'riskyOperation',
userAction: 'button-click',
additionalContext: {
sceneId: canvas.scene?.id,
tokenCount: canvas.tokens?.controlled?.length
}
});
}

// Still handle the error normally
console.error('Operation failed:', error);
ui.notifications.error('Operation failed. Please try again.');
}

Integration Patterns

Error Boundaries

Wrap risky operations with error boundaries:

class MyModuleFeature {
async performComplexOperation() {
try {
const result = await this.riskyAsyncOperation();
return result;
} catch (error) {
// Report error while preserving normal error handling
this.reportError(error, { operation: 'performComplexOperation' });

// Handle error normally
throw error; // Re-throw if calling code should handle it
// OR provide fallback behavior
}
}

reportError(error, context = {}) {
const errorReporter = game.modules.get('errors-and-echoes');
if (errorReporter?.active && errorReporter.api?.hasConsent()) {
errorReporter.api.report(error, {
module: this.moduleId,
context: {
...context,
timestamp: Date.now(),
userAction: this.currentUserAction,
},
});
}
}
}

Hook Error Reporting

Report errors that occur in Foundry hooks:

Hooks.on('updateActor', (actor, data, options, userId) => {
try {
myModule.handleActorUpdate(actor, data, options, userId);
} catch (error) {
// Report hook errors with context
window.ErrorsAndEchoesAPI?.reportError(error, {
hook: 'updateActor',
actorId: actor.id,
updateData: data,
userId: userId
});

// Don't prevent other modules from handling the hook
console.error('Error in updateActor hook:', error);
}
});

Custom Endpoints

Configure a custom error reporting endpoint for your modules:

Hooks.on('errorsAndEchoesReady', (api) => {
api.register({
moduleId: 'my-module',
endpoint: {
name: 'My Module Error Reporting',
url: 'https://errors.my-domain.com/report/my-module',
author: 'my-username',
modules: ['my-module', 'my-other-module'],
enabled: true,
},
});
});

See Endpoint Setup for details on creating your own error reporting endpoint.

Testing Integration

Test Registration

Verify your module is registered:

// Check if your module is registered
const isRegistered = ModuleRegistry.isRegistered('my-module');
console.log('Module registered:', isRegistered);

// Get registration details
const registration = ModuleRegistry.getRegisteredModule('my-module');
console.log('Registration:', registration);

Test Error Reporting

Generate a test error to verify reporting works:

// Test manual error reporting
try {
throw new Error('Test error from my-module');
} catch (error) {
window.ErrorsAndEchoesAPI.reportError(error, {
test: true,
module: 'my-module'
});
}

Test Context Provider

Verify your context provider returns appropriate data:

// Test context provider
const registration = ModuleRegistry.getRegisteredModule('my-module');
if (registration?.contextProvider) {
const context = registration.contextProvider();
console.log('Context:', context);

// Verify no sensitive data is included
console.log('Context size:', JSON.stringify(context).length);
}

Integration Examples

Integration examples are available in the Examples Repository:

Best Practices

Registration Timing

  • Register during the ready hook for best compatibility
  • Check if the API exists before registering
  • Only register once per module

Privacy Compliance

  • Never collect personally identifiable information
  • Provide clear context about what data you're collecting
  • Respect user privacy level settings
  • Follow GDPR and other privacy regulations

Error Handling

  • Never let error reporting break your module's functionality
  • Always handle errors gracefully even if reporting fails
  • Don't report the same error repeatedly
  • Filter out expected errors and user errors

Performance

  • Keep context providers lightweight and fast
  • Avoid expensive operations in context providers
  • Limit context data size to essential information
  • Use error filters to reduce unnecessary reports

Next Steps