diff --git a/lib/db-ctl-base.c b/lib/db-ctl-base.c
index 033011c15..14cdff6eb 100644
--- a/lib/db-ctl-base.c
+++ b/lib/db-ctl-base.c
@@ -2191,6 +2191,25 @@ ctl_might_write_to_db(const struct ctl_command *commands, size_t n)
     return false;
 }
 
+/* Report an error while running in the command context. Caller should return
+ * to its caller immediately after reporting the error. */
+void
+ctl_error(struct ctl_context *ctx, const char *format, ...)
+{
+    va_list args;
+
+    ovs_assert(ctx);
+
+    if (ctx->error) {
+        VLOG_ERR("Discarding unhandled error: %s", ctx->error);
+        free(ctx->error);
+    }
+
+    va_start(args, format);
+    ctx->error = xvasprintf(format, args);
+    va_end(args);
+}
+
 void
 ctl_fatal(const char *format, ...)
 {
@@ -2376,6 +2395,7 @@ ctl_context_init_command(struct ctl_context *ctx,
     ds_swap(&ctx->output, &command->output);
     ctx->table = command->table;
     ctx->try_again = false;
+    ctx->error = NULL;
 }
 
 /* Initializes the entire 'ctx'. */
@@ -2401,6 +2421,8 @@ ctl_context_done_command(struct ctl_context *ctx,
 {
     ds_swap(&ctx->output, &command->output);
     command->table = ctx->table;
+    free(ctx->error);
+    ctx->error = NULL;
 }
 
 /* Finishes up with 'ctx'.
diff --git a/lib/db-ctl-base.h b/lib/db-ctl-base.h
index 8c6ac19a3..5d325d8e7 100644
--- a/lib/db-ctl-base.h
+++ b/lib/db-ctl-base.h
@@ -59,6 +59,8 @@ void ctl_init__(const struct ovsdb_idl_class *, const struct ctl_table_class *,
                 const struct cmd_show_table *cmd_show_tables,
                 void (*ctl_exit_func)(int status));
 char *ctl_default_db(void);
+void ctl_error(struct ctl_context *, const char *, ...)
+OVS_PRINTF_FORMAT(2, 3);
 OVS_NO_RETURN void ctl_fatal(const char *, ...) OVS_PRINTF_FORMAT(1, 2);
 
 /* *ctl command syntax structure, to be defined by each command implementation.
@@ -225,6 +227,7 @@ struct ctl_context {
     struct shash options;
 
     /* Modifiable state. */
+    char *error;
     struct ds output;
     struct table *table;
     struct ovsdb_idl *idl;
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
index 798f6bef4..63d41660b 100644
--- a/ovn/utilities/ovn-nbctl.c
+++ b/ovn/utilities/ovn-nbctl.c
@@ -3788,6 +3788,9 @@ run_prerequisites(struct ctl_command *commands, size_t n_commands,
 
             ctl_context_init(&ctx, c, idl, NULL, NULL, NULL);
             (c->syntax->prerequisites)(&ctx);
+            if (ctx.error) {
+                ctl_fatal("%s", ctx.error);
+            }
             ctl_context_done(&ctx, c);
 
             ovs_assert(!c->output.string);
@@ -3837,6 +3840,9 @@ do_nbctl(const char *args, struct ctl_command *commands, size_t n_commands,
         if (c->syntax->run) {
             (c->syntax->run)(&ctx);
         }
+        if (ctx.error) {
+            ctl_fatal("%s", error);
+        }
         ctl_context_done_command(&ctx, c);
 
         if (ctx.try_again) {
@@ -3875,6 +3881,9 @@ do_nbctl(const char *args, struct ctl_command *commands, size_t n_commands,
             if (c->syntax->postprocess) {
                 ctl_context_init(&ctx, c, idl, txn, symtab, NULL);
                 (c->syntax->postprocess)(&ctx);
+                if (ctx.error) {
+                    ctl_fatal("%s", ctx.error);
+                }
                 ctl_context_done(&ctx, c);
             }
         }
diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c
index 0e2c67c08..441b2dac6 100644
--- a/ovn/utilities/ovn-sbctl.c
+++ b/ovn/utilities/ovn-sbctl.c
@@ -1238,6 +1238,9 @@ run_prerequisites(struct ctl_command *commands, size_t n_commands,
 
             sbctl_context_init(&sbctl_ctx, c, idl, NULL, NULL);
             (c->syntax->prerequisites)(&sbctl_ctx.base);
+            if (sbctl_ctx.base.error) {
+                ctl_fatal("%s", sbctl_ctx.base.error);
+            }
             sbctl_context_done(&sbctl_ctx, c);
 
             ovs_assert(!c->output.string);
@@ -1281,6 +1284,9 @@ do_sbctl(const char *args, struct ctl_command *commands, size_t n_commands,
         if (c->syntax->run) {
             (c->syntax->run)(&sbctl_ctx.base);
         }
+        if (sbctl_ctx.base.error) {
+            ctl_fatal("%s", sbctl_ctx.base.error);
+        }
         sbctl_context_done_command(&sbctl_ctx, c);
 
         if (sbctl_ctx.base.try_again) {
@@ -1316,6 +1322,9 @@ do_sbctl(const char *args, struct ctl_command *commands, size_t n_commands,
             if (c->syntax->postprocess) {
                 sbctl_context_init(&sbctl_ctx, c, idl, txn, symtab);
                 (c->syntax->postprocess)(&sbctl_ctx.base);
+                if (sbctl_ctx.base.error) {
+                    ctl_fatal("%s", sbctl_ctx.base.error);
+                }
                 sbctl_context_done(&sbctl_ctx, c);
             }
         }
diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c
index 6a9637a20..e845634c2 100644
--- a/utilities/ovs-vsctl.c
+++ b/utilities/ovs-vsctl.c
@@ -2527,6 +2527,9 @@ run_prerequisites(struct ctl_command *commands, size_t n_commands,
 
             vsctl_context_init(&vsctl_ctx, c, idl, NULL, NULL, NULL);
             (c->syntax->prerequisites)(&vsctl_ctx.base);
+            if (vsctl_ctx.base.error) {
+                ctl_fatal("%s", vsctl_ctx.base.error);
+            }
             vsctl_context_done(&vsctl_ctx, c);
 
             ovs_assert(!c->output.string);
@@ -2621,6 +2624,9 @@ do_vsctl(const char *args, struct ctl_command *commands, size_t n_commands,
         if (c->syntax->run) {
             (c->syntax->run)(&vsctl_ctx.base);
         }
+        if (vsctl_ctx.base.error) {
+            ctl_fatal("%s", vsctl_ctx.base.error);
+        }
         vsctl_context_done_command(&vsctl_ctx, c);
 
         if (vsctl_ctx.base.try_again) {
@@ -2659,6 +2665,9 @@ do_vsctl(const char *args, struct ctl_command *commands, size_t n_commands,
             if (c->syntax->postprocess) {
                 vsctl_context_init(&vsctl_ctx, c, idl, txn, ovs, symtab);
                 (c->syntax->postprocess)(&vsctl_ctx.base);
+                if (vsctl_ctx.base.error) {
+                    ctl_fatal("%s", vsctl_ctx.base.error);
+                }
                 vsctl_context_done(&vsctl_ctx, c);
             }
         }
diff --git a/vtep/vtep-ctl.c b/vtep/vtep-ctl.c
index 678323fae..e0d5121a3 100644
--- a/vtep/vtep-ctl.c
+++ b/vtep/vtep-ctl.c
@@ -2260,6 +2260,9 @@ run_prerequisites(struct ctl_command *commands, size_t n_commands,
 
             vtep_ctl_context_init(&vtepctl_ctx, c, idl, NULL, NULL, NULL);
             (c->syntax->prerequisites)(&vtepctl_ctx.base);
+            if (vtepctl_ctx.base.error) {
+                ctl_fatal("%s", vtepctl_ctx.base.error);
+            }
             vtep_ctl_context_done(&vtepctl_ctx, c);
 
             ovs_assert(!c->output.string);
@@ -2304,6 +2307,9 @@ do_vtep_ctl(const char *args, struct ctl_command *commands,
         if (c->syntax->run) {
             (c->syntax->run)(&vtepctl_ctx.base);
         }
+        if (vtepctl_ctx.base.error) {
+            ctl_fatal("%s", vtepctl_ctx.base.error);
+        }
         vtep_ctl_context_done_command(&vtepctl_ctx, c);
 
         if (vtepctl_ctx.base.try_again) {
@@ -2339,6 +2345,9 @@ do_vtep_ctl(const char *args, struct ctl_command *commands,
             if (c->syntax->postprocess) {
                 vtep_ctl_context_init(&vtepctl_ctx, c, idl, txn, vtep_global, symtab);
                 (c->syntax->postprocess)(&vtepctl_ctx.base);
+                if (vtepctl_ctx.base.error) {
+                    ctl_fatal("%s", vtepctl_ctx.base.error);
+                }
                 vtep_ctl_context_done(&vtepctl_ctx, c);
             }
         }
