diff --git a/sdk/nodejs/cmd/run/run.ts b/sdk/nodejs/cmd/run/run.ts index 5db651bef..6cefdd40d 100644 --- a/sdk/nodejs/cmd/run/run.ts +++ b/sdk/nodejs/cmd/run/run.ts @@ -163,20 +163,25 @@ export function run(argv: minimist.ParsedArgs): void { // Set up the process uncaught exception, unhandled rejection, and program exit handlers. let uncaught: Error | undefined; const uncaughtHandler = (err: Error) => { + // Default message should be to include the full stack (which includes the message), or + // fallback to just the message if we can't get the stack. + const defaultMessage = err.stack || err.message; + // First, log the error. if (RunError.isInstance(err)) { - // For errors that are subtypes of RunError, we will print the message without hitting the unhandled error - // logic, which will dump all sorts of verbose spew like the origin source and stack trace. - log.error(err.message); + // Hide the stack if requested to by the RunError creator. + const message = err.hideStack ? err.message : defaultMessage; + log.error(message, err.resource); } else { log.error(`Running program '${program}' failed with an unhandled exception:`); - log.error(err.stack || err.message); + log.error(defaultMessage); } // Remember that we failed with an error. Don't quit just yet so we have a chance to drain the message loop. uncaught = err; }; + process.on("uncaughtException", uncaughtHandler); process.on("unhandledRejection", uncaughtHandler); diff --git a/sdk/nodejs/errors.ts b/sdk/nodejs/errors.ts index fa8342957..2f7c0d1dd 100644 --- a/sdk/nodejs/errors.ts +++ b/sdk/nodejs/errors.ts @@ -12,9 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { Resource } from "./resource"; + /** - * RunError can be used for terminating a program abruptly, but resulting in a clean exit rather than the usual - * verbose unhandled error logic which emits the source program text and complete stack trace. + * RunError can be used for terminating a program abruptly, optionally associating the problem with + * a Resource. Depending on the nature of the problem, clients can choose whether or not a + * callstack should be returned as well. This should be very rare, and would only indicate no + * usefulness of presenting that stack to the user. */ export class RunError extends Error { /** @@ -31,8 +35,7 @@ export class RunError extends Error { return obj && obj.__pulumiRunError; } - constructor(message: string) { + constructor(message: string, public resource?: Resource, public hideStack?: boolean) { super(message); } } -