Repository: sindresorhus/got
Pull request #398 (2017-10-30 10:09:31)

Use statusMessage returned by endpoint
for cases when endpoints returns a descriptive error message

sindresorhus: Can you include a test?
sleewoo: cranking a test
lukechilds: I support this change but just wanna point out potential breakage.

Any existing user code like:

```js
if (err.statusMessage === 'Too Many Requests') {
  handleAPILimit();
}
```

will fail after this change if the API returns it's own error message.

Patch:
diff --git a/index.js b/index.js
index d1668f0..0cdcc00 100644
--- a/index.js
+++ b/index.js
@@ -324,7 +324,7 @@ function asPromise(opts) {
 					}
 
 					if (statusCode !== 304 && (statusCode < 200 || statusCode > limitStatusCode)) {
-						throw new got.HTTPError(statusCode, res.headers, opts);
+						throw new got.HTTPError(statusCode, res.statusMessage, res.headers, opts);
 					}
 
 					resolve(res);
@@ -407,7 +407,7 @@ function asStream(opts) {
 		res.pipe(output);
 
 		if (statusCode !== 304 && (statusCode < 200 || statusCode > 299)) {
-			proxy.emit('error', new got.HTTPError(statusCode, res.headers, opts), null, res);
+			proxy.emit('error', new got.HTTPError(statusCode, res.statusMessage, res.headers, opts), null, res);
 			return;
 		}
 
@@ -627,8 +627,12 @@ got.ParseError = class extends StdError {
 };
 
 got.HTTPError = class extends StdError {
-	constructor(statusCode, headers, opts) {
-		const statusMessage = http.STATUS_CODES[statusCode];
+	constructor(statusCode, statusMessage, headers, opts) {
+		if (statusMessage) {
+			statusMessage = statusMessage.replace(/\r?\n/g, ' ').trim();
+		} else {
+			statusMessage = http.STATUS_CODES[statusCode];
+		}
 		super(`Response code ${statusCode} (${statusMessage})`, {}, opts);
 		this.name = 'HTTPError';
 		this.statusCode = statusCode;
diff --git a/package.json b/package.json
index 0644a38..2ef78ca 100644
--- a/package.json
+++ b/package.json
@@ -70,7 +70,7 @@
 		"url-to-options": "^1.0.1"
 	},
 	"devDependencies": {
-		"ava": "^0.22.0",
+		"ava": "^0.23.0",
 		"coveralls": "^3.0.0",
 		"form-data": "^2.1.1",
 		"get-port": "^3.0.0",


Test patch:
diff --git a/test/error.js b/test/error.js
index b9d7ab0..f9a116a 100644
--- a/test/error.js
+++ b/test/error.js
@@ -14,6 +14,17 @@ test.before('setup', async () => {
 		res.end('not');
 	});
 
+	s.on('/default-status-message', (req, res) => {
+		res.statusCode = 400;
+		res.end('body');
+	});
+
+	s.on('/custom-status-message', (req, res) => {
+		res.statusCode = 400;
+		res.statusMessage = 'Something Exploded';
+		res.end('body');
+	});
+
 	await s.listen(s.port);
 });
 
@@ -45,6 +56,18 @@ test('options.body error message', async t => {
 	t.regex(err.message, /options\.body must be a ReadableStream, string, Buffer or plain Object/);
 });
 
+test('default status message', async t => {
+	const err = await t.throws(got(`${s.url}/default-status-message`));
+	t.is(err.statusCode, 400);
+	t.is(err.statusMessage, 'Bad Request');
+});
+
+test('custom status message', async t => {
+	const err = await t.throws(got(`${s.url}/custom-status-message`));
+	t.is(err.statusCode, 400);
+	t.is(err.statusMessage, 'Something Exploded');
+});
+
 test.serial('http.request error', async t => {
 	const stub = sinon.stub(http, 'request').callsFake(() => {
 		throw new TypeError('The header content contains invalid characters');