diff --git a/src/ajax/xhr.js b/src/ajax/xhr.js index 55d526a41..bdeeee3f8 100644 --- a/src/ajax/xhr.js +++ b/src/ajax/xhr.js @@ -112,10 +112,15 @@ jQuery.ajaxTransport(function( options ) { // Create the abort callback callback = xhrCallbacks[ id ] = callback("abort"); - // Do send the request - // This may raise an exception which is actually - // handled in jQuery.ajax (so no try/catch here) - xhr.send( options.hasContent && options.data || null ); + try { + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } }, abort: function() { diff --git a/test/unit/ajax.js b/test/unit/ajax.js index 77648404c..c7d7daba4 100644 --- a/test/unit/ajax.js +++ b/test/unit/ajax.js @@ -1544,6 +1544,36 @@ module( "ajax", { }); } + ajaxTest( "#14683 - jQuery.ajax() - Exceptions thrown synchronously by xhr.send should be caught", 4, [ + { + url: "data/params_html.php", + method: "POST", + data: { + toString: function() { + throw "Can't parse"; + } + }, + processData: false, + done: function( data ) { + ok( false, "done: " + data ); + }, + fail: function( jqXHR, status, error ) { + ok( true, "exception caught: " + error ); + strictEqual( jqXHR.status, 0, "proper status code" ); + strictEqual( status, "error", "proper status" ); + } + }, + { + url: "http://domain.org:80d", + done: function( data ) { + ok( false, "done: " + data ); + }, + fail: function( _, status, error ) { + ok( true, "fail: " + status + " - " + error ); + } + } + ]); + //----------- jQuery.ajaxPrefilter() ajaxTest( "jQuery.ajaxPrefilter() - abort", 1, {