-
Notifications
You must be signed in to change notification settings - Fork 197
Error handling practice
UserError: errors caused by user's input/behavior, which can fixed by user's proper behavior. For example:
- User's subscription has no permission on some resources
- Invalid inputs
- Concurrent operation error, please wait until the running task finishs or you can reload the window to cancel it.
SystemError: errors caused by system bugs or failures, which can not fixed by user. For example,
- Cannot read property ' [*] ' of undefined
For Errors thrown by third-party modules, it's better to analyze and classify the Error types:
failed to exec dotnet script.,errorStack=Failed to install .NET Core SDK (v3.1). Install .NET Core SDK (v3.1) manually and restart Visual Studio Code
Frequent UserError means that user experience of the product should be improved to decrease the possibility of user using the toolkit in the wrong way. Frequent SysteError means that the system should reduce bugs to improve the quality and increase the usability.
There are many ways to define customized errors in the project:
- Define an error as a constant value
- Define an error as a function that returns a
UserError
orSystemError
- Define an customized error class
- directly construct a
UserError
orSystemError
in the place where error happens- construct error (
UserError
/SystemError
) with name, message,source,... - construct error (
UserError
/SystemError
) with existing Error object - construct error (
UserError
/SystemError
) with option (UserErrorOptions
/SystemErrorOptions
) object
- construct error (
We don't suggest first method, unless you don't care about the stack at all. Because the error stack is constant and meaningless in such a case.
We don't suggest the second method neither, which add one more call stack on top of the stacks where the error really happens.
Here is a bad sample:
function MyError() {
return new UserError("MyError", "my error message", "API");
}
console.log(MyError());
The error stack will contains the function MyError
, which is not expected:
We suggest the third method to write your own error class that extends UserError
or SystemError
.
For example:
class MyError extends UserError {
constructor () {
super(new.target.name, "my error message", "API")
}
}
Then the stack will be clean:
Another advantage of the third method is that it support instanceof
operation on error type checking:
console.log(new MyError() instanceof MyError); // output `true`
Because in the constructor, we have passed new.target.name
(which is actually MyError
) as error name parameter into the super
constructor.
Alternatively, if we don't want to define an error name different from the class name, we can just pass an empty string as the error name.
The following definition is equivalent to previous one:
class MyError extends UserError {
constructor () {
super("", "my error message", "API")
}
}
The reason is the in constructor of UserError
/SystemError
, we have some priority checking on error name. Explicitly input name is the first priority, then the name of Error
object is the second priority. If both are empty, we will use the constructor name as the default error name:
this.name = option.name || (option.error && option.error.name) || new.target.name;
In addition to method three, We also suggest the fourth method, because it is the most flexible.
We have three override constructors for UserError
or SystemError
.
const error = new UserError(myName, myMessage, mySource, undefined, myHelpLink);
chai.assert.equal(error.name, myName);
chai.assert.equal(error.message, myMessage);
chai.assert.equal(error.source, mySource);
chai.assert.equal(error.helpLink, myHelpLink);
chai.assert.isDefined(error.timestamp);
chai.assert.isTrue(error instanceof UserError);
const innerError = new RangeError(myMessage);
const error = new UserError(innerError, mySource);
chai.assert.equal(error.name, "RangeError");
chai.assert.equal(error.message, myMessage);
chai.assert.equal(error.source, mySource);
chai.assert.isTrue(error instanceof UserError);
chai.assert.equal(error.innerError, innerError);
When constructing with existing Error object, the name of the input Error will have higher priority than the constructor name.
This approach provides most flexible way to construct a Error:
const error = new UserError({ error: new RangeError(myMessage), source: mySource, helpLink: myHelpLink });
chai.assert.equal(error.name, "RangeError");
chai.assert.equal(error.message, myMessage);
chai.assert.equal(error.source, mySource);
chai.assert.equal(error.helpLink, myHelpLink);
chai.assert.isTrue(error instanceof UserError);
You can input an empty option to build an error, the constructor will replace empty values with some default values:
const error = new UserError({});
chai.assert.equal(error.name, "UserError");
chai.assert.equal(error.message, "");
chai.assert.equal(error.source, "unknown");
chai.assert.isTrue(error instanceof UserError);
Build Custom Engine Copilots
- Build a basic AI chatbot for Teams
- Build an AI agent chatbot for Teams
- Expand AI bot's knowledge with your content
Scenario-based Tutorials
- Send notifications to Teams
- Respond to chat commands in Teams
- Respond to card actions in Teams
- Embed a dashboard canvas in Teams
Extend your app across Microsoft 365
- Teams tabs in Microsoft 365 and Outlook
- Teams message extension for Outlook
- Add Outlook Add-in to a Teams app
App settings and Microsoft Entra Apps
- Manage Application settings with Teams Toolkit
- Manage Microsoft Entra Application Registration with Teams Toolkit
- Use an existing Microsoft Entra app
- Use a multi-tenant Microsoft Entra app
Configure multiple capabilities
- How to configure Tab capability within your Teams app
- How to configure Bot capability within your Teams app
- How to configure Message Extension capability within your Teams app
Add Authentication to your app
- How to add single sign on in Teams Toolkit for Visual Studio Code
- How to enable Single Sign-on in Teams Toolkit for Visual Studio
Connect to cloud resources
- How to integrate Azure Functions with your Teams app
- How to integrate Azure API Management
- Integrate with Azure SQL Database
- Integrate with Azure Key Vault
Deploy apps to production