Errors, Reason Codes and Enums

KOS is an application-based platform that is designed for collaboration. Unlike most operating systems where applications are sandboxed and protected from each other, KOS is designed to allow applications to tightly integrate and work together. As multiple vendors contribute code to a product, it can introduce challenges around values that are typically enumerated. Common examples are error codes and shared Java enums.

Reason codes

Error codes are an easy example of the complexities of shared platforms. While using numeric error codes is a common technique, it requires a registry of existing error codes to avoid using the same error code for different errors in different parts of the code. Monolithic projects make this process relatively straightforward, using a single class as the master list of all error codes. However, numeric and cryptic error codes have several challenges when used on a shared platform:

  • As different organizations can contribute applications to a device, there is no global registry for error codes, meaning that it’s entirely possible for different applications to return the same errors with different meanings.
  • Developers using 3rd party applications require considerable documentation around error codes to map them from codes to actions, as the codes themselves tend not to describe the error.
  • There is often a need to indicate states that aren’t errors, which also require some type of code. For example, a resource may become unavailable for several reasons, none of which are errors, but there is still a need to capture the reason.

KOS extensively uses reason codes to represent both errors and reason states. Reason codes are short strings that are self-descriptive, human-readable, and easily processed in code. The following are examples of reason codes that represent errors:

// reason codes
private static final String REASON_errUnsupportedSchema = "errUnsupportedSchema";
private static final String REASON_errFailedToLoadTest  = "errFailedToLoadTest";
private static final String REASON_errSoldOut           = "errSoldOut";

KOS uses an “err” prefix for reasons that are used as an error code, but this prefix is optional as not all reasons are errors. KOS also uses the unusual practice of defining string constants in mixed case for reason codes. Defining the constant using the same value as the code itself avoids a two-step lookup in the source code to find where the errors are returned.

By using self-describing reason codes, there is no need to define a global registry of codes, and each code module can define the reason codes it requires without worrying about overlapping codes, as the codes only need to make sense in the context of the code that uses them. This not only drastically simplifies error code management but also provides more usable information to 3rd party consumers of the codes, allowing for faster and easier integrations.

Dangers of enums

Java enums are a useful abstraction for defining a constrained range of values. However, when working with devices that can be updated and downgraded, as well as be constructed as a mix of 3rd party applications, enums have a critical limitation that should be understood before using them.

Standard Java enums will throw an exception when presented with a value not part of the enum. This can cause issues when mixing different versions of data and code. Consider the following scenarios:

  • A config value is set to an enum value, and then newer code is loaded, which no longer contains the configured enum. This will throw an exception when trying to configure the new version of the code.
  • A data file contains a new enum value, but it is installed on a device still running old code. Attempting to load the data file will throw an exception due to the missing enum value in the old code.

In extreme cases, using enums for critical update code can prevent updates or rollbacks from working correctly.

KOS is very careful about using enums and uses string values in various critical code paths.