Answer
Opera’s Fetch API does not place the AbortSignal.reason value on the error object that rejects the fetch promise, whether the abort comes from an explicit controller.abort() call or from a timeout‑initiated abort via AbortSignal.timeout(). The reason remains available on the AbortSignal instance itself (signal.reason) in both cases.
Confirmed facts
- The rejected fetch promise yields an
AbortError (a DOMException) with error.name === 'AbortError' and a generic message; the error object has no reason property.
- The original
AbortSignal passed to fetch retains signal.reason exactly as set (custom value for controller.abort() or the timeout message for AbortSignal.timeout()).
- When Opera Desktop and Opera Android are built from the same Chromium version, their behavior is identical; no observable difference has been reported between the two platforms.
Likely explanation
Chromium’s networking stack wraps an abort in an AbortError without copying the signal’s reason into the exception. The signal object is kept alive by the fetch algorithm, so the reason can still be read after the fetch settles.
Verification steps
- Open the Opera console (Desktop or Android via remote debugging).
- Run the following snippet:
async function testAbort(useTimeout) {
const controller = new AbortController();
const signal = controller.signal;
// set a custom reason
signal.reason = { timeout: useTimeout, ts: Date.now() };
let error = null;
try {
if (useTimeout) {
// abort after 30 ms via timeout signal
await fetch('https://example.com', { signal, timeout: 30 });
} else {
// explicit abort after 20 ms
setTimeout(() => controller.abort(), 20);
await fetch('https://example.com', { signal });
}
} catch (e) {
error = e;
}
console.log('error.name:', error?.name);
console.log('error.message:', error?.message);
console.log('error has reason?', Object.hasOwn(error, 'reason'));
console.log('signal.reason:', signal.reason);
}
// Test explicit abort
testAbort(false);
// Test timeout abort
testAbort(true);
- Observe that
error.name is "AbortError" and there is no reason property on the error object.
- Confirm that
signal.reason contains the value you set (custom object or timeout message).
Missing diagnostic detail
To be certain that Desktop and Android behave the same, please confirm the Chromium version numbers for your Opera Desktop and Opera Android builds (e.g., opera://about output). If the versions differ, subtle differences in abort‑signal handling could appear.