diff --git a/lib/libhbsdcontrol/aslr.c b/lib/libhbsdcontrol/aslr.c
index 1d1879147d13..d1ee5b44778f 100644
--- a/lib/libhbsdcontrol/aslr.c
+++ b/lib/libhbsdcontrol/aslr.c
@@ -64,6 +64,7 @@ hbsdctrl_feature_aslr_new(hbsdctrl_ctx_t *ctx, hbsdctrl_flag_t flags)
 
 	feature = hbsdctrl_feature_new(ctx, "aslr", flags);
 	if (feature == NULL) {
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_MEM);
 		return (NULL);
 	}
 
@@ -115,6 +116,7 @@ hbsdctrl_feature_aslr_validate(hbsdctrl_ctx_t *ctx, hbsdctrl_feature_t *feature,
 	 */
 
 	if (arg1 == NULL && arg2 == NULL) {
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_EINVAL);
 		return (RES_FAIL);
 	}
 
@@ -122,6 +124,7 @@ hbsdctrl_feature_aslr_validate(hbsdctrl_ctx_t *ctx, hbsdctrl_feature_t *feature,
 		memset(&state, 0, sizeof(state));
 		res = feature->hf_get(ctx, feature, arg1, &state);
 		if (res != RES_SUCCESS) {
+			/* We assume the callback has properly set the error */
 			return (res);
 		}
 
@@ -130,6 +133,7 @@ hbsdctrl_feature_aslr_validate(hbsdctrl_ctx_t *ctx, hbsdctrl_feature_t *feature,
 
 	if (hbsdctrl_feature_state_value_valid(
 	    hbsdctrl_feature_state_get_value(arg2)) == false) {
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_EINVAL);
 		return (RES_FAIL);
 	}
 
@@ -146,6 +150,7 @@ hbsdctrl_feature_aslr_apply(hbsdctrl_ctx_t *ctx, hbsdctrl_feature_t *feature __u
 	int fd;
 
 	if (arg1 == NULL || arg2 == NULL) {
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_EINVAL);
 		return (RES_FAIL);
 	}
 
@@ -153,23 +158,27 @@ hbsdctrl_feature_aslr_apply(hbsdctrl_ctx_t *ctx, hbsdctrl_feature_t *feature __u
 	fd = *(__DECONST(int *, arg1));
 
 	if (fd < 0) {
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_EINVAL);
 		return (RES_FAIL);
 	}
 
 	value = hbsdctrl_feature_state_get_value(state);
 	if (!hbsdctrl_feature_state_value_valid(value)) {
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_BADSTATE);
 		return (RES_FAIL);
 	}
 
 	buf[0] = (value == HBSDCTRL_STATE_ENABLED) ? '1' : '0';
 	if (extattr_set_fd(fd, ctx->hc_namespace, ATTRNAME_ENABLED, buf,
 	    sizeof(buf)) != sizeof(buf)) {
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_SYSCALL);
 		return (RES_FAIL);
 	}
 
 	buf[0] = (value == HBSDCTRL_STATE_DISABLED) ? '1' : '0';
 	if (extattr_set_fd(fd, ctx->hc_namespace, ATTRNAME_DISABLED, buf,
 	    sizeof(buf)) != sizeof(buf)) {
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_SYSCALL);
 		return (RES_FAIL);
 	}
 
@@ -185,6 +194,7 @@ hbsdctrl_feature_aslr_unapply(hbsdctrl_ctx_t *ctx, hbsdctrl_feature_t *feature _
 	int fd;
 
 	if (arg1 == NULL) {
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_EINVAL);
 		return (RES_FAIL);
 	}
 
@@ -192,11 +202,13 @@ hbsdctrl_feature_aslr_unapply(hbsdctrl_ctx_t *ctx, hbsdctrl_feature_t *feature _
 
 	if (extattr_delete_fd(fd, ctx->hc_namespace, ATTRNAME_ENABLED)
 	    && errno != ENOATTR) {
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_SYSCALL);
 		return (RES_FAIL);
 	}
 
 	if (extattr_delete_fd(fd, ctx->hc_namespace, ATTRNAME_DISABLED)
 	    && errno != ENOATTR) {
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_SYSCALL);
 		return (RES_FAIL);
 	}
 
@@ -214,6 +226,7 @@ hbsdctrl_feature_aslr_get(hbsdctrl_ctx_t *ctx, hbsdctrl_feature_t *feature __unu
 	int fd;
 
 	if (arg1 == NULL || arg2 == NULL) {
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_EINVAL);
 		return (RES_FAIL);
 	}
 
@@ -230,6 +243,7 @@ hbsdctrl_feature_aslr_get(hbsdctrl_ctx_t *ctx, hbsdctrl_feature_t *feature __unu
 			*/
 			return (RES_SUCCESS);
 		}
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_SYSCALL);
 		return (RES_FAIL);
 	}
 
@@ -237,17 +251,20 @@ hbsdctrl_feature_aslr_get(hbsdctrl_ctx_t *ctx, hbsdctrl_feature_t *feature __unu
 
 	if (sz > 2) {
 		hbsdctrl_feature_state_set_value(res, HBSDCTRL_STATE_INVALID);
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_BADSTATE);
 		return (RES_FAIL);
 	}
 
 	buf = calloc(1, sz);
 	if (buf == NULL) {
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_MEM);
 		return (RES_FAIL);
 	}
 
 	sz = extattr_get_fd(fd, ctx->hc_namespace, ATTRNAME_ENABLED, buf, sz);
 	if (sz < 0) {
 		free(buf);
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_SYSCALL);
 		hbsdctrl_feature_state_set_value(res, HBSDCTRL_STATE_INVALID);
 		return (RES_FAIL);
 	}
@@ -258,22 +275,26 @@ hbsdctrl_feature_aslr_get(hbsdctrl_ctx_t *ctx, hbsdctrl_feature_t *feature __unu
 	sz = extattr_get_fd(fd, ctx->hc_namespace, ATTRNAME_DISABLED, NULL, 0);
 	if (sz <= 0) {
 		hbsdctrl_feature_state_set_value(res, HBSDCTRL_STATE_INVALID);
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_SYSCALL);
 		return (RES_FAIL);
 	}
 
 	if (sz > 2) {
 		hbsdctrl_feature_state_set_value(res, HBSDCTRL_STATE_INVALID);
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_BADSTATE);
 		return (RES_FAIL);
 	}
 
 	buf = calloc(1, sz);
 	if (buf == NULL) {
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_MEM);
 		return (RES_FAIL);
 	}
 
 	sz = extattr_get_fd(fd, ctx->hc_namespace, ATTRNAME_DISABLED, buf, sz);
 	if (sz < 0) {
 		free(buf);
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_SYSCALL);
 		hbsdctrl_feature_state_set_value(res, HBSDCTRL_STATE_INVALID);
 		return (RES_FAIL);
 	}
@@ -283,6 +304,7 @@ hbsdctrl_feature_aslr_get(hbsdctrl_ctx_t *ctx, hbsdctrl_feature_t *feature __unu
 
 	if (enabled && disabled) {
 		hbsdctrl_feature_state_set_value(res, HBSDCTRL_STATE_INVALID);
+		hbsdctrl_ctx_set_error(ctx, HBSDCTRL_ERROR_BADSTATE);
 		return (RES_FAIL);
 	}
 
@@ -292,12 +314,13 @@ hbsdctrl_feature_aslr_get(hbsdctrl_ctx_t *ctx, hbsdctrl_feature_t *feature __unu
 		hbsdctrl_feature_state_set_value(res, HBSDCTRL_STATE_DISABLED);
 	}
 
+	hbsdctrl_ctx_clear_error(ctx);
 	return (RES_SUCCESS);
 }
 
 static hbsdctrl_feature_cb_res_t
 hbsdctrl_feature_aslr_help(hbsdctrl_ctx_t *ctx __unused,
-    hbsdctrl_feature_t *feature __unused, const void *arg1 __unused,
+    hbsdctrl_feature_t *feature __unused, const void *arg1,
     void *arg2)
 {
 	const char *prefix;
diff --git a/lib/libhbsdcontrol/libhbsdcontrol.c b/lib/libhbsdcontrol/libhbsdcontrol.c
index 2b6550bc0471..5d19c277a83f 100644
--- a/lib/libhbsdcontrol/libhbsdcontrol.c
+++ b/lib/libhbsdcontrol/libhbsdcontrol.c
@@ -37,6 +37,7 @@
 
 #include "libhbsdcontrol.h"
 
+static bool _hbsdctrl_error_check_sanity(hbsdctrl_error_t);
 static hbsdctrl_feature_cb_res_t _hbsdctrl_default_feature_success_cb(
     hbsdctrl_ctx_t *, hbsdctrl_feature_t *, const void *, void *);
 
@@ -61,6 +62,8 @@ hbsdctrl_ctx_new(hbsdctrl_flag_t flags, const char *ns)
 		return (NULL);
 	}
 
+	ctx->hc_error = HBSDCTRL_ERROR_NONE;
+
 	ctx->hc_namespace = -1;
 	if (extattr_string_to_namespace(ns, &(ctx->hc_namespace))) {
 		free(ctx);
@@ -342,6 +345,82 @@ hbsdctrl_ctx_free_feature_names(char **names)
 	free(names);
 }
 
+hbsdctrl_error_t
+hbsdctrl_ctx_get_error(hbsdctrl_ctx_t *ctx)
+{
+	if (ctx == NULL) {
+		return (HBSDCTRL_ERROR_NONE);
+	}
+
+	return (ctx->hc_error);
+}
+
+hbsdctrl_error_t
+hbsdctrl_ctx_set_error(hbsdcctrl_ctx_t *ctx, hbsdctrl_error_t error)
+{
+	hbsdctrl_error_t olderror;
+
+	if (ctx == NULL) {
+		return (HBSDCTRL_ERROR_NONE);
+	}
+
+	if (!_hbsdctrl_error_check_sanity(error)) {
+		/* Do not permit setting an invalid error */
+		return (ctx->hc_error);
+	}
+
+	olderror = ctx->hc_error;
+	ctx->hc_error = error;
+
+	return (olderror);
+}
+
+hbsdctrl_error_t
+hbsdctrl_ctx_clear_error(hbsdctrl_ctx_t *ctx)
+{
+	hbsdctrl_error_t olderror;
+
+	if (ctx == NULL) {
+		return (HBSDCTRL_ERROR_NONE);
+	}
+
+	olderror = ctx->hc_error;
+	ctx->hc_error = HBSDCTRL_ERROR_NONE;
+
+	return (olderror);
+}
+
+bool
+hbsddctrl_ctx_has_error(hbsdctrl_ctx_t *ctx)
+{
+	if (ctx == NULL) {
+		return (false);
+	}
+
+	return (ctx->hc_error != HBSDCTRL_ERROR_NONE);
+}
+
+const char *
+hbsdctrl_error_to_string(hbsdctrl_error_t error)
+{
+	switch (error) {
+	case HBSDCTRL_ERROR_NONE:
+		return ("none");
+	case HBSDCTRL_ERROR_MEM:
+		return ("memory");
+	case HBSDCTRL_ERROR_NS:
+		return ("namespace");
+	case HBSDCTRL_ERROR_SYSCALL:
+		return ("syscall");
+	case HBSDCTRL_ERROR_EINVAL:
+		return ("invalid argument");
+	case HBSDCTRL_ERROR_BADSTATE:
+		return ("bad state");
+	default:
+		return ("unknown");
+	}
+}
+
 hbsdctrl_feature_t *
 hbsdctrl_feature_new(hbsdctrl_ctx_t *ctx, const char *name, hbsdctrl_flag_t flags)
 {
@@ -930,3 +1009,19 @@ _hbsdctrl_default_feature_success_cb(hbsdctrl_ctx_t *ctx __unused,
 {
 	return (RES_SUCCESS);
 }
+
+static bool
+_hbsdctrl_error_check_sanity(hbsdctrl_error_t error)
+{
+	switch (error) {
+	case HBSDCTRL_ERROR_NONE:
+	case HBSDCTRL_ERROR_MEM:
+	case HBSDCTRL_ERROR_NS:
+	case HBSDCTRL_ERROR_SYSCALL:
+	case HBSDCTRL_ERROR_EINVAL:
+	case HBSDCTRL_ERROR_BADSTATE:
+		return (true);
+	default:
+		return (false);
+	}
+}
diff --git a/lib/libhbsdcontrol/libhbsdcontrol.h b/lib/libhbsdcontrol/libhbsdcontrol.h
index 946c301fae7f..7f4b21acc837 100644
--- a/lib/libhbsdcontrol/libhbsdcontrol.h
+++ b/lib/libhbsdcontrol/libhbsdcontrol.h
@@ -35,7 +35,7 @@
 
 #include <sys/queue.h>
 
-#define LIBHBSDCONTROL_VERSION			20240316001UL
+#define LIBHBSDCONTROL_VERSION			20240316002UL
 #define LIBHBSDCONTROL_DEFAULT_NAMESPACE	"system"
 
 #define HBSDCTRL_FEATURE_STATE_FLAG_NONE	0x0UL
@@ -71,6 +71,15 @@ typedef enum _hbsdctrl_feature_state_value {
 	HBSDCTRL_STATE_INVALID	= 4,
 } hbsdctrl_feature_state_value_t;
 
+typedef enum _hbsdctrl_error {
+	HBSDCTRL_ERROR_NONE	= 0,
+	HBSDCTRL_ERROR_MEM	= 1,
+	HBSDCTRL_ERROR_NS	= 2,
+	HBSDCTRL_ERROR_SYSCALL	= 3,
+	HBSDCTRL_ERROR_EINVAL	= 4,
+	HBSDCTRL_ERROR_BADSTATE	= 5,
+} hbsdctrl_error_t;
+
 /*
  * Each feature implements callbacks for various operations. Arguments:
  *     1. Pointer to the hbsdcontrol context object
@@ -90,7 +99,8 @@ struct _hbsdctrl_ctx {
 	int				 hc_namespace;
 	pthread_mutex_t			 hc_mtx;
 	LIST_HEAD(,_hbsdctrl_feature)	 hc_features;
-	uint64_t			 hc_spare[32];
+	hbsdctrl_error_t		 hc_error;
+	uint64_t			 hc_spare[31];
 };
 
 struct _hbsdctrl_feature {
@@ -144,12 +154,21 @@ hbsdctrl_feature_t *hbsdctrl_ctx_find_feature_by_name(hbsdctrl_ctx_t *,
 char **hbsdctrl_ctx_all_feature_names(hbsdctrl_ctx_t *);
 void hbsdctrl_ctx_free_feature_names(char **);
 
-hbsdctrl_feature_t *hbsdctrl_feature_new(hbsdctrl_ctx_t *, const char *, hbsdctrl_flag_t);
+hbsdctrl_error_t hbsdctrl_ctx_get_error(hbsdctrl_ctx_t *);
+hbsdctrl_error_t hbsdctrl_ctx_set_error(hbsdctrl_ctx_t *, hbsdctrl_error_t);
+hbsdctrl_error_t hbsdctrl_ctx_clear_error(hbsdctrl_ctx_t *);
+bool hbsdctrl_ctx_has_error(hbsdctrl_ctx_t *);
+const char *hbsdctrl_error_to_string(hbsdctrl_error_t);
+
+hbsdctrl_feature_t *hbsdctrl_feature_new(hbsdctrl_ctx_t *, const char *,
+    hbsdctrl_flag_t);
 void hbsdctrl_feature_free(hbsdctrl_feature_t **);
 bool hbsdctrl_feature_flag_sanity(hbsdctrl_flag_t);
 hbsdctrl_flag_t hbsdctrl_feature_get_flags(const hbsdctrl_feature_t *);
-hbsdctrl_flag_t hbsdctrl_feature_set_flag(hbsdctrl_feature_t *, hbsdctrl_flag_t);
-hbsdctrl_flag_t hbsdctrl_feature_set_flags(hbsdctrl_feature_t *, hbsdctrl_flag_t);
+hbsdctrl_flag_t hbsdctrl_feature_set_flag(hbsdctrl_feature_t *,
+    hbsdctrl_flag_t);
+hbsdctrl_flag_t hbsdctrl_feature_set_flags(hbsdctrl_feature_t *,
+    hbsdctrl_flag_t);
 bool hbsdctrl_feature_is_flag_set(const hbsdctrl_feature_t *, hbsdctrl_flag_t);
 
 const char *hbsdctrl_feature_get_name(const hbsdctrl_feature_t *);
