necolas/react-native-web · warning
Cannot record touch end without a touch start. Touch End: ${
Error message
Cannot record touch end without a touch start.
Touch End: ${printTouch(touch)}
Touch Bank: ${printTouchBank(touchHistory)} What it means
This warning is emitted by ResponderTouchHistoryStore when recordTouchEnd receives a touch whose identifier is not in the touch bank — a 'touchend' arrives for a touch that never had a recorded 'touchstart'. The store warns via console.warn and skips updating history, so the dangling touch never completes a gesture. Like error 10, it signals inconsistent touch event ordering rather than a crash.
Source
Thrown at packages/react-native-web/src/modules/useResponderEvents/ResponderTouchHistoryStore.js:140
`Touch Move: ${printTouch(touch)}\n`,
`Touch Bank: ${printTouchBank(touchHistory)}`
);
}
}
function recordTouchEnd(touch: Touch, touchHistory): void {
const touchRecord = touchHistory.touchBank[getTouchIdentifier(touch)];
if (touchRecord) {
touchRecord.touchActive = false;
touchRecord.previousPageX = touchRecord.currentPageX;
touchRecord.previousPageY = touchRecord.currentPageY;
touchRecord.previousTimeStamp = touchRecord.currentTimeStamp;
touchRecord.currentPageX = touch.pageX;
touchRecord.currentPageY = touch.pageY;
touchRecord.currentTimeStamp = timestampForTouch(touch);
touchHistory.mostRecentTimeStamp = timestampForTouch(touch);
} else {
console.warn(
'Cannot record touch end without a touch start.\n',
`Touch End: ${printTouch(touch)}\n`,
`Touch Bank: ${printTouchBank(touchHistory)}`
);
}
}
function printTouch(touch: Touch): string {
return JSON.stringify({
identifier: touch.identifier,
pageX: touch.pageX,
pageY: touch.pageY,
timestamp: timestampForTouch(touch)
});
}
function printTouchBank(touchHistory): string {
const { touchBank } = touchHistory;View on GitHub (pinned to a9de220ba9)
Solutions
- Dispatch a matching touchstart (same identifier) before touchend in any synthetic event code
- Handle touchcancel: if the platform sends touchcancel instead of touchend, don't forward a touchend to responder events afterward
- Ensure listener/mount lifecycle spans the entire gesture — don't unmount or re-key the responder element mid-touch
- Clear or reset ResponderTouchHistoryStore state between test runs so stale/cancelled touches don't corrupt subsequent assertions
Example fix
// before
dispatchTouchEvent('touchend', { identifier: 1 });
// after
dispatchTouchEvent('touchstart', { identifier: 1, pageX: 10, pageY: 20 });
dispatchTouchEvent('touchend', { identifier: 1, pageX: 10, pageY: 20 }); Defensive patterns
Strategy: validation
Validate before calling
function hasTouchStart(touchHistory, identifier) {
return touchHistory != null && touchHistory.touchBank != null && touchHistory.touchBank[identifier] != null;
}
if (!hasTouchStart(touchHistory, touch.identifier)) return; // skip end
recordTouchEnd(touch, touchHistory); Type guard
function isTrackedTouch(touchHistory, touch) {
return Boolean(touch && typeof touch.identifier === 'number' && touchHistory?.touchBank?.[touch.identifier]);
} Prevention
- Pair every touchend with a preceding touchstart of the same identifier in synthetic event code
- Treat touchcancel as terminal; do not emit touchend after a cancel for the same touch
- Keep the responder element mounted for the full gesture duration; avoid re-keying mid-touch
- Clear touch history state between automated test runs
When it happens
Trigger: A touchend/touchchange event whose touch.identifier has no entry in touchHistory.touchBank; caused by missing or swallowed touchstart, listener attachment after the gesture began, or synthetic event dispatch that ends a touch it never started.
Common situations: Test automation firing touchend without a preceding touchstart; fast taps where the browser coalesces or cancels events (touchcancel not handled by the test harness); page navigation/hydration occurring between touchstart and touchend; custom gesture libraries replaying only end events.
Related errors
AI-assisted analysis of necolas/react-native-web@a9de220ba9 (2026-09-01).
Data as JSON: /api/errors/68816299bd02b403.
Report an issue: GitHub.