"apierror-notarget": "You have not specified a valid target for this action.",
"apierror-notpatrollable": "The revision r$1 can't be patrolled as it's too old.",
"apierror-nouploadmodule": "No upload module set.",
+ "apierror-offline": "Could not proceed due to network connectivity issues. Make sure you have a working internet connection and try again.",
"apierror-opensearch-json-warnings": "Warnings cannot be represented in OpenSearch JSON format.",
"apierror-pagecannotexist": "Namespace doesn't allow actual pages.",
"apierror-pagedeleted": "The page has been deleted since you fetched its timestamp.",
"apierror-stashzerolength": "File is of zero length, and could not be stored in the stash: $1.",
"apierror-systemblocked": "You have been blocked automatically by MediaWiki.",
"apierror-templateexpansion-notwikitext": "Template expansion is only supported for wikitext content. $1 uses content model $2.",
+ "apierror-timeout": "The server did not respond within the expected time.",
"apierror-toofewexpiries": "$1 expiry {{PLURAL:$1|timestamp was|timestamps were}} provided where $2 {{PLURAL:$2|was|were}} needed.",
"apierror-unknownaction": "The action specified, <kbd>$1</kbd>, is not recognized.",
"apierror-unknownerror-editpage": "Unknown EditPage error: $1.",
"apierror-notarget": "{{doc-apierror}}",
"apierror-notpatrollable": "{{doc-apierror}}\n\nParameters:\n* $1 - Revision ID number.",
"apierror-nouploadmodule": "{{doc-apierror}}",
+ "apierror-offline": "Error message for when files could not be uploaded as a result of bad/lost internet connection.",
"apierror-opensearch-json-warnings": "{{doc-apierror}}",
"apierror-pagecannotexist": "{{doc-apierror}}",
"apierror-pagedeleted": "{{doc-apierror}}",
"apierror-stashzerolength": "{{doc-apierror}}\n\nParameters:\n* $1 - Exception text. Currently this is probably English, hopefully we'll fix that in the future.",
"apierror-systemblocked": "{{doc-apierror}}",
"apierror-templateexpansion-notwikitext": "{{doc-apierror}}\n\nParameters:\n* $1 - Page title.\n* $2 - Content model.",
+ "apierror-timeout": "API error message that can be used for client side localisation of API errors.",
"apierror-toofewexpiries": "{{doc-apierror}}\n\nParameters:\n* $1 - Number provided.\n* $2 - Number needed.",
"apierror-unknownaction": "{{doc-apierror}}\n\nParameters:\n* $1 - Action provided.",
"apierror-unknownerror-editpage": "{{doc-apierror}}\n\nParameters:\n* $1 - Error code (an integer).",
'action-upload',
'apierror-mustbeloggedin',
'badaccess-groups',
+ 'apierror-timeout',
+ 'apierror-offline',
'apierror-unknownerror',
'api-error-unknown-warning',
'fileexists',
stateDetails = this.upload.getStateDetails(),
error = stateDetails.errors ? stateDetails.errors[ 0 ] : false,
warnings = stateDetails.upload && stateDetails.upload.warnings,
- $ul = $( '<ul>' );
+ $ul = $( '<ul>' ),
+ errorText;
if ( state === mw.Upload.State.ERROR ) {
if ( !error ) {
+ if ( stateDetails.textStatus === 'timeout' ) {
+ // in case of $.ajax.fail(), there is no response json
+ errorText = mw.message( 'apierror-timeout' ).parse();
+ } else if ( stateDetails.xhr && stateDetails.xhr.status === 0 ) {
+ // failed to even connect to server
+ errorText = mw.message( 'apierror-offline' ).parse();
+ } else if ( stateDetails.textStatus ) {
+ errorText = stateDetails.textStatus;
+ } else {
+ errorText = mw.message( 'apierror-unknownerror', JSON.stringify( stateDetails ) ).parse();
+ }
+
// If there's an 'exception' key, this might be a timeout, or other connection problem
return $.Deferred().resolve( new OO.ui.Error(
- $( '<p>' ).msg( 'apierror-unknownerror', JSON.stringify( stateDetails ) ),
+ $( '<p>' ).html( errorText ),
{ recoverable: false }
) );
}