Skip to content

Error Handling

ResourceLoader.js has two separate places where errors surface: the onError callback (per-resource, fires immediately) and the Promise rejection (fires after the entire batch finishes). You can use both together.

Fires immediately when a specific resource fails, before the overall Promise settles. Use this to show inline error UI or log individual failures:

ResourceLoader.include(['https://cdn.example.com/app.js'], {
onError: (error, url) => {
// error.type: 'network' | 'timeout' | 'abort' | 'unsupported'
// error.message: human-readable description
// url: the URL that failed
console.warn(`Failed to load ${url}: [${error.type}] ${error.message}`);
},
});

When any resource in the batch fails, the Promise rejects with an aggregate error after all resources have either succeeded or failed. This lets you handle the overall result in one place:

ResourceLoader.include([
'https://cdn.example.com/app.css',
'https://cdn.example.com/bad-script.js', // This will fail
]).catch((aggregateError) => {
console.log(aggregateError.type); // 'aggregate'
console.log(aggregateError.message); // 'One or more resources failed to load.'
console.log(aggregateError.results);
// [
// { status: 'fulfilled', value: 'https://cdn.example.com/app.css' },
// { status: 'rejected', reason: { type: 'network', message: '...' }, url: 'https://cdn.example.com/bad-script.js' }
// ]
});

Both the onError callback and individual reason entries in the aggregate use the same error shape:

{
type: 'network' | 'timeout' | 'abort' | 'unsupported',
message: string
}
{
type: 'aggregate',
message: 'One or more resources failed to load.',
results: [
// One entry per URL, in the same order as the input array
{ status: 'fulfilled', value: 'https://...' },
{ status: 'rejected', reason: { type: 'network', message: '...' }, url: 'https://...' }
]
}

TypeWhen it occursRetried?
networkHTTP error (404, 500), resource not found, or no connectionYes
timeoutLoad time exceeded the timeout optionYes
abortcancelResource() or cancelAll() was calledNo
unsupportedFile extension is not in the supported listNo

ResourceLoader.include(['https://api.example.com/data.json'], {
timeout: 5000,
onError: (error, url) => {
switch (error.type) {
case 'network':
showBanner('Could not load data. Check your internet connection.');
break;
case 'timeout':
showBanner('Request timed out. Please try again later.');
break;
case 'abort':
console.log('Load was cancelled by the application.');
break;
case 'unsupported':
console.error(`Unsupported file type for URL: ${url}`);
break;
}
},
});

When you need to know which resources succeeded and which failed:

ResourceLoader.include([
'https://cdn.example.com/a.css',
'https://cdn.example.com/b.css',
'https://example.com/c.css', // 404
]).catch((error) => {
if (error.type !== 'aggregate') throw error; // unexpected error — rethrow
const succeeded = error.results.filter(r => r.status === 'fulfilled');
const failed = error.results.filter(r => r.status === 'rejected');
console.log(`${succeeded.length} succeeded, ${failed.length} failed`);
failed.forEach(({ reason, url }) => {
console.error(` ${url}${reason.type}: ${reason.message}`);
});
});

If you pass a URL with an extension that ResourceLoader.js doesn’t recognize, the load fails immediately with type: 'unsupported':

ResourceLoader.include(['https://example.com/data.xml'], {
onError: (error, url) => {
console.error(error.type); // 'unsupported'
console.error(error.message); // 'Unsupported resource type: xml'
},
}).catch(() => {}); // Promise also rejects

See Supported File Types for the full list of supported extensions.


When a <script> or <link> load fails, the element is automatically removed from the DOM (default: removeFailedElements: true). This prevents broken element stubs from cluttering the page.

Set to false only for debugging purposes:

ResourceLoader.include(['https://example.com/missing.js'], {
removeFailedElements: false, // Keep the <script> element for inspection
});

Here is a production-ready pattern that handles all cases:

async function loadAppResources() {
try {
const results = await ResourceLoader.include([
'https://cdn.example.com/app.css',
'https://api.example.com/config.json',
'https://cdn.example.com/app.js',
], {
retries: 2,
timeout: 10000,
onError: (error, url) => {
// Log each failure as it happens — useful for monitoring
analytics.track('resource_load_error', {
url,
errorType: error.type,
message: error.message,
});
},
});
// All resources loaded successfully
const config = results.find(r => typeof r.value === 'object')?.value ?? {};
startApp(config);
} catch (error) {
if (error.type === 'aggregate') {
const criticalFailed = error.results.some(
r => r.status === 'rejected' && r.url.endsWith('app.js')
);
if (criticalFailed) {
showFatalError('The application could not load. Please refresh the page.');
} else {
console.warn('Some non-critical resources failed. Continuing with degraded mode.');
startApp({});
}
} else {
throw error; // Unexpected error type — let it propagate
}
}
}