Merge branch 'master' of github.com:openwhisk/openwhisk-client-js
diff --git a/lib/client.js b/lib/client.js
index e1060c8..47feba2 100644
--- a/lib/client.js
+++ b/lib/client.js
@@ -1,9 +1,10 @@
 'use strict'
 
 const messages = require('./messages')
-const BaseOperationError = require('./base_operation_error')
+const OpenWhiskError = require('./openwhisk_error')
 const rp = require('request-promise')
 const url = require('url')
+const http = require('http')
 
 class Client {
   constructor (options) {
@@ -73,53 +74,33 @@
   }
 
   handle_errors (reason) {
+    let message = `Unknown Error From API: ${reason.message}`
     if (reason.hasOwnProperty('statusCode')) {
-      let error
-      switch (reason.statusCode) {
-        case 400:
-          error = messages.BAD_REQUEST_ERROR
-          break
-        case 401:
-        case 403:
-          error = messages.INVALID_AUTH_ERROR
-          break
-        case 404:
-          error = messages.MISSING_URL_ERROR
-          break
-        case 408:
-          error = messages.INVOKE_TIMEOUT_ERROR
-          break
-        case 409:
-          error = messages.CREATE_CONFLICT_ERROR
-          break
-        case 502:
-          error = messages.ACTION_INVOCATION_ERROR
-          break
-        default:
-          error = messages.API_SYSTEM_ERROR
-      }
-
-      throw new BaseOperationError(`${error} ${this.err_message(reason.error)}`, reason.error)
+      const responseError = this.err_message(reason.error)
+      message = `${reason.options.method} ${reason.options.url} Returned HTTP ${reason.statusCode} (${http.STATUS_CODES[reason.statusCode]}) --> "${responseError}"`
     }
 
-    throw new Error(`Error encountered calling OpenWhisk: ${reason.message}`)
+    throw new OpenWhiskError(message, reason.error)
   }
 
+  // Error messages might be returned from platform or using custom
+  // invocation result response from action.
   err_message (error) {
-    if (!error) return 'Missing error message.'
+    if (!error) return 'Response Missing Error Message.'
 
     if (typeof error.error === 'string') {
       return error.error
-    } else if (error.response && error.response.result) {
-      const result = error.response.result
-      if (typeof result.error === 'string') {
-        return result.error
-      } else if (typeof result.statusCode === 'number') {
-        return `application error, status code: ${result.statusCode}`
+    } else if (error.response && error.response.result 
+      && error.response.result.error) {
+      const err = error.response.result.error
+      if (typeof err === 'string') {
+        return err
+      } else if (typeof err.error === 'string') {
+        return err.error
       }
     }
 
-    return 'Missing error message.'
+    return 'Response Missing Error Message.'
   }
 }
 
diff --git a/lib/messages.js b/lib/messages.js
index b690b36..49b3501 100644
--- a/lib/messages.js
+++ b/lib/messages.js
@@ -14,13 +14,6 @@
   MISSING_TRIGGER_BODY_ERROR: 'Missing mandatory trigger parameter from options.',
   MISSING_PACKAGE_BODY_ERROR: 'Missing mandatory package parameter from options.',
   MISSING_NAMESPACE_ERROR: 'Missing namespace from options, please set a default namespace or pass one in the options.',
-  INVOKE_TIMEOUT_ERROR: 'Action invocation timed out before completing.',
-  CREATE_CONFLICT_ERROR: 'Conflict detected updating resource.',
-  INVALID_AUTH_ERROR: 'OpenWhisk authentication failed, check API key?',
-  BAD_REQUEST_ERROR: 'API call failed due to invalid request payload.',
-  MISSING_URL_ERROR: 'Invalid URL for API called, OpenWhisk returned HTTP 404 response.',
-  API_SYSTEM_ERROR: 'API call failed, response contained error code.',
-  ACTION_INVOCATION_ERROR: 'Action invocation failed, API returned error code.',
   INVALID_OPTIONS_ERROR: 'Invalid constructor options.',
   MISSING_BASEPATH_ERROR: 'Missing mandatory basepath parameter from options.'
 }
diff --git a/lib/base_operation_error.js b/lib/openwhisk_error.js
similarity index 76%
rename from lib/base_operation_error.js
rename to lib/openwhisk_error.js
index 0cbfedd..bec6dcd 100644
--- a/lib/base_operation_error.js
+++ b/lib/openwhisk_error.js
@@ -1,10 +1,10 @@
 'use strict';
 
-module.exports = function BaseOperationError(message, error) {
+module.exports = function OpenWhiskError(message, error) {
   Error.captureStackTrace(this, this.constructor);
   this.name = this.constructor.name;
   this.message = message;
   this.error = error;
 };
 
-require('util').inherits(module.exports, Error);
\ No newline at end of file
+require('util').inherits(module.exports, Error);
diff --git a/test/unit/client.test.js b/test/unit/client.test.js
index 16bed22..285c6e5 100644
--- a/test/unit/client.test.js
+++ b/test/unit/client.test.js
@@ -2,6 +2,7 @@
 
 const test = require('ava')
 const Client = require('../../lib/client')
+const http = require('http')
 
 test('should use default constructor options', t => {
   const client = new Client({api_key: 'aaa', apihost: 'my_host'})
@@ -98,21 +99,18 @@
   t.is(client.auth_header(), `Basic ${Buffer.from(api_key).toString('base64')}`)
 })
 
-test('should throw errors for HTTP response failures', t => {
+test('should return path and status code in error message', t => {
   const client = new Client({api_key: true, api: true})
-  t.throws(() => client.handle_errors({statusCode: 400}), /invalid request/)
-  t.throws(() => client.handle_errors({statusCode: 400, error: { error: 'some msg'}}), /some msg/)
-  t.throws(() => client.handle_errors({statusCode: 401}), /authentication failed/)
-  t.throws(() => client.handle_errors({statusCode: 403}), /authentication failed/)
-  t.throws(() => client.handle_errors({statusCode: 404}), /HTTP 404/)
-  t.throws(() => client.handle_errors({statusCode: 408}), /timed out/)
-  t.throws(() => client.handle_errors({statusCode: 409}), /Conflict/)
-  t.throws(() => client.handle_errors({statusCode: 500, error: {}}), /API call failed/)
-  t.throws(() => client.handle_errors({statusCode: 500, error: {response: {result: {error: 'custom'}}}}), /custom/)
-  t.throws(() => client.handle_errors({statusCode: 500, error: {response: {result: {statusCode: 404}}}}), /404/)
-  t.throws(() => client.handle_errors({statusCode: 502, error: {}}), /Action invocation failed/)
-  t.throws(() => client.handle_errors({statusCode: 502, error: {response: {result: {error: 'custom'}}}}), /custom/)
-  t.throws(() => client.handle_errors({statusCode: 502, error: {response: {result: {statusCode: 404}}}}), /404/)
+  const method = 'METHOD', url = 'https://blah.com/api/v1/actions/list', statusCode = 400
+  t.throws(() => client.handle_errors({options: { method, url }, statusCode }), `${method} ${url} Returned HTTP ${statusCode} (${http.STATUS_CODES[statusCode]}) --> "Response Missing Error Message."`)
+})
+
+test('should return response error string in error message', t => {
+  const client = new Client({api_key: true, api: true})
+  const method = 'METHOD', url = 'https://blah.com/api/v1/actions/list', statusCode = 400
+  t.throws(() => client.handle_errors({error: { error: 'hello' }, options: { method, url }, statusCode }), `${method} ${url} Returned HTTP ${statusCode} (${http.STATUS_CODES[statusCode]}) --> "hello"`)
+  t.throws(() => client.handle_errors({error: { response: { result: { error: 'hello' } } }, options: { method, url }, statusCode }), `${method} ${url} Returned HTTP ${statusCode} (${http.STATUS_CODES[statusCode]}) --> "hello"`)
+  t.throws(() => client.handle_errors({error: { response: { result: { error: { error: 'hello' } } } }, options: { method, url }, statusCode }), `${method} ${url} Returned HTTP ${statusCode} (${http.STATUS_CODES[statusCode]}) --> "hello"`)
 })
 
 test('should throw errors for non-HTTP response failures', t => {