Skip to content
D
Documentation

Cancel requests

how-to
2 min readUpdated

Use cancellation when a request is no longer needed, or when an extended client needs a different cancellation lifetime. Ky builds on Fetch: pass an AbortController's signal through the signal option, then call abort() to cancel.

1. Pass a signal and handle cancellation

This sample runs in a browser or Node.js with Fetch support. It follows the authors' cancellation example: start a request, cancel it with a timer, and distinguish cancellation from other failures. Use a slow endpoint so the request is still pending when the timer fires.

ts
import ky from 'ky';

async function main() {
	const controller = new AbortController();
	const timer = setTimeout(() => {
		controller.abort();
	}, 500);

	try {
		const text = await ky.get('https://httpbin.org/delay/2', {
			signal: controller.signal,
		}).text();
		console.log(text);
	} catch (error) {
		if (error instanceof Error && error.name === 'AbortError') {
			console.log('Fetch aborted');
		} else {
			console.error('Fetch error:', error);
		}
	} finally {
		clearTimeout(timer);
	}
}

void main();

With a pending request, abort() makes the awaited operation reject with an error named AbortError, and the cancellation branch prints Fetch aborted. If the request finishes first, .text() returns the response body as a string instead. Keep other failures in a separate branch rather than treating every rejection as cancellation.

2. Replace an inherited signal

By default, extend() combines inherited and new signals: either controller can cancel the extended client's requests. Use replaceOption when only the new controller must control the child client.

ts
import ky, {replaceOption} from 'ky';

async function main() {
	const parentController = new AbortController();
	const replacementController = new AbortController();
	const api = ky.create({signal: parentController.signal});
	const childApi = api.extend({
		signal: replaceOption(replacementController.signal),
	});

	parentController.abort();

	try {
		const text = await childApi.get('https://httpbin.org/get').text();
		console.log(text);
	} catch (error) {
		console.error('Fetch error:', error);
	}

	const pending = childApi.get('https://httpbin.org/delay/2').text();
	replacementController.abort();

	try {
		await pending;
	} catch (error) {
		if (error instanceof Error && error.name === 'AbortError') {
			console.log('Child request aborted');
		} else {
			console.error('Fetch error:', error);
		}
	}
}

void main();

Aborting the parent does not cancel the child's first request. Look for your endpoint's response text. The replacement controller cancels the subsequent child request. extend() returns a new client; the parent retains its original defaults.

3. Remove an inherited signal

Pass signal: undefined to extend() to remove inherited signals without installing a replacement. The child can make requests even after the parent controller is aborted.

ts
import ky from 'ky';

async function main() {
	const parentController = new AbortController();
	const api = ky.create({signal: parentController.signal});
	const independentApi = api.extend({signal: undefined});

	parentController.abort();

	try {
		const text = await independentApi.get('https://httpbin.org/get').text();
		console.log(text);
	} catch (error) {
		console.error('Fetch error:', error);
	}
}

void main();

The parent signal no longer cancels this request. You can still pass a fresh signal in an individual request's options to make that request cancellable.

Options and pitfalls

The cancellation option belongs to Options.

OptionTypeDefaultWhat it does
signalAbortSignal | null | undefinedNot setSupplies a Fetch cancellation signal. In extend(), a new signal combines with inherited signals; replaceOption(signal) replaces them, and explicit undefined removes them.
  • Create a fresh controller for a new cancellation lifetime. A request using an already-aborted signal rejects rather than starting a fresh operation.
  • Signal combination requires AbortSignal.any(). Without it, Ky uses the last supplied signal instead of combining signals.
  • The AbortError check above applies to abort() without a custom reason. If you pass a reason to abort(reason), handle that reason: Ky surfaces the user signal's abort reason.

Was this page helpful?

Cancel requests — ky · GPT-6.1 Sol