diff --git a/src/backends/glr.c b/src/backends/glr.c
index 535dc2860c59018324893da1450cfc4ff4fadf8b..44b0c50cafd08486866eedf17e29c50236434f9b 100644
--- a/src/backends/glr.c
+++ b/src/backends/glr.c
@@ -14,7 +14,7 @@ int h_glr_compile(HAllocator* mm__, HParser* parser, const void* params)
   }
   int result = h_lalr_compile(mm__, parser, params);
 
-  if(result == -1 && parser->backend_data) {
+  if(result == -2 && parser->backend_data) {
     // table is there, just has conflicts? nevermind, that's okay.
     result = 0;
   }
diff --git a/src/backends/lalr.c b/src/backends/lalr.c
index b82ef71c477128728db39d4ac72ef8d4ab0dc56c..db9b88ae28caf5d39bf134df454add37577d86c8 100644
--- a/src/backends/lalr.c
+++ b/src/backends/lalr.c
@@ -279,18 +279,18 @@ int h_lalr_compile(HAllocator* mm__, HParser* parser, const void* params)
   }
   HCFGrammar *g = h_cfgrammar_(mm__, h_desugar_augmented(mm__, parser));
   if(g == NULL)     // backend not suitable (language not context-free)
-    return -1;
+    return 2;
 
   HLRDFA *dfa = h_lr0_dfa(g);
   if (dfa == NULL) {     // this should normally not happen
     h_cfgrammar_free(g);
-    return -1;
+    return 3;
   }
 
   HLRTable *table = h_lr0_table(g, dfa);
   if (table == NULL) {   // this should normally not happen
     h_cfgrammar_free(g);
-    return -1;
+    return 4;
   }
 
   if(has_conflicts(table)) {
@@ -300,7 +300,7 @@ int h_lalr_compile(HAllocator* mm__, HParser* parser, const void* params)
     if(eg == NULL) {    // this should normally not happen
       h_cfgrammar_free(g);
       h_lrtable_free(table);
-      return -1;
+      return 5;
     }
 
     // go through the inadequate states; replace inadeq with a new list
@@ -349,7 +349,7 @@ int h_lalr_compile(HAllocator* mm__, HParser* parser, const void* params)
 
   h_cfgrammar_free(g);
   parser->backend_data = table;
-  return has_conflicts(table)? -1 : 0;
+  return has_conflicts(table)? -2 : 0;
 }
 
 void h_lalr_free(HParser *parser)
diff --git a/src/backends/llk.c b/src/backends/llk.c
index 4e8209b30f4aa7bd97f5df1c49202643d4efedd4..19944a20930eef5fcb71efd3d055aebeb93e9cfa 100644
--- a/src/backends/llk.c
+++ b/src/backends/llk.c
@@ -238,7 +238,7 @@ int h_llk_compile(HAllocator* mm__, HParser* parser, const void* params)
     // the table was ambiguous
     h_cfgrammar_free(grammar);
     h_llktable_free(table);
-    return -1;
+    return -2;
   }
   parser->backend_data = table;
 
diff --git a/src/backends/regex.c b/src/backends/regex.c
index f26abfda67af76900010053c6a6003fad1df55e7..0337949f948e06f332bbbada27170c4e99fae2ef 100644
--- a/src/backends/regex.c
+++ b/src/backends/regex.c
@@ -430,9 +430,10 @@ static int h_regex_compile(HAllocator *mm__, HParser* parser, const void* params
   prog->actions = NULL;
   prog->allocator = mm__;
   if (setjmp(prog->except)) {
-    return false;
+    return 3;
   }
   if (!h_compile_regex(prog, parser)) {
+    // this shouldn't normally fail when isValidRegular() returned true
     h_free(prog->insns);
     h_free(prog->actions);
     h_free(prog);
diff --git a/src/benchmark.c b/src/benchmark.c
index b6a2876fa0a1a85711c610b1d2bc5f1143c77f87..7d56c32e7c17bd204fd76b1cdd5b8d6680aeea57 100644
--- a/src/benchmark.c
+++ b/src/benchmark.c
@@ -46,7 +46,7 @@ HBenchmarkResults *h_benchmark__m(HAllocator* mm__, HParser* parser, HParserTest
   for (backend = PB_MIN; backend <= PB_MAX; backend++) {
     ret->results[backend].backend = backend;
     // Step 1: Compile grammar for given parser...
-    if (h_compile(parser, backend, NULL) == -1) {
+    if (h_compile(parser, backend, NULL)) {
       // backend inappropriate for grammar...
       fprintf(stderr, "Compiling for %s failed\n", HParserBackendNames[backend]);
       ret->results[backend].compile_success = false;
diff --git a/src/hammer.h b/src/hammer.h
index c8a1074c9a18f45e96b66989e8fc13b54735afff..6cd2660d3cfd29a9b4d34e1e054d2613ca4260a2 100644
--- a/src/hammer.h
+++ b/src/hammer.h
@@ -785,7 +785,13 @@ void h_pprintln(FILE* stream, const HParsedToken* tok);
  * documentation for the parser backend in question for information
  * about the [params] parameter, or just pass in NULL for the defaults.
  *
- * Returns -1 if grammar cannot be compiled with the specified options; 0 otherwise.
+ * Returns a nonzero value on error; 0 otherwise. Common return codes include:
+ *
+ *  -1: parser uses a combinator that is incompatible with the chosen backend.
+ *  -2: parser could not be compiled with the chosen parameters.
+ *  >0: unexpected internal errors.
+ *
+ * Consult each backend for details.
  */
 HAMMER_FN_DECL(int, h_compile, HParser* parser, HParserBackend backend, const void* params);