diff --git a/modcc/expression.cpp b/modcc/expression.cpp
index 51e8217b5147109af2dfff553a71889f25b11ef4..336acf91609bc385e2d34803dbd39218b9062241 100644
--- a/modcc/expression.cpp
+++ b/modcc/expression.cpp
@@ -79,6 +79,7 @@ std::string LocalVariable::to_string() const {
 *******************************************************************************/
 
 void IdentifierExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
 
     auto s = scope_->find(spelling_);
@@ -142,6 +143,8 @@ expression_ptr DerivativeExpression::clone() const {
 }
 
 void DerivativeExpression::semantic(scope_ptr scp) {
+    error_ = false;
+
     IdentifierExpression::semantic(scp);
     auto v = symbol_->is_variable();
     if (!v || !v->is_state()) {
@@ -198,6 +201,7 @@ bool LocalDeclaration::add_variable(Token tok) {
 }
 
 void LocalDeclaration::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
 
     // loop over the variables declared in this LOCAL statement
@@ -239,6 +243,7 @@ std::string ArgumentExpression::to_string() const {
 }
 
 void ArgumentExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
 
     auto s = scope_->find(name_);
@@ -303,12 +308,25 @@ expression_ptr ReactionExpression::clone() const {
 }
 
 void ReactionExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
+
     lhs()->semantic(scp);
     rhs()->semantic(scp);
 
     fwd_rate()->semantic(scp);
     rev_rate()->semantic(scp);
+
+    std::string msg = lhs_->has_error() ? lhs_->error_message() :
+                      rhs_->has_error() ? rhs_->error_message() :
+                      fwd_rate_->has_error() ? fwd_rate_->error_message() :
+                      rev_rate_->has_error() ? rev_rate_->error_message() : "";
+
+    if (!msg.empty()) {
+        error(msg);
+        return;
+    }
+
     if(fwd_rate_->is_procedure_call() || rev_rate_->is_procedure_call()) {
         error("procedure calls can't be made in an expression");
     }
@@ -324,8 +342,13 @@ expression_ptr StoichTermExpression::clone() const {
 }
 
 void StoichTermExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
+
     ident()->semantic(scp);
+    if(ident()->has_error()) {
+        error(ident()->error_message());
+    }
 }
 
 /*******************************************************************************
@@ -353,9 +376,14 @@ std::string StoichExpression::to_string() const {
 }
 
 void StoichExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
+
     for(auto& e: terms()) {
         e->semantic(scp);
+        if(e->has_error()) {
+            error(e->error_message());
+        }
     }
 }
 
@@ -387,8 +415,13 @@ std::string CompartmentExpression::to_string() const {
 }
 
 void CompartmentExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
+
     scale_factor()->semantic(scp);
+    if(scale_factor()->has_error()) {
+        error(scale_factor()->error_message());
+    }
 }
 
 /*******************************************************************************
@@ -401,10 +434,19 @@ expression_ptr LinearExpression::clone() const {
 }
 
 void LinearExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
+
     lhs_->semantic(scp);
     rhs_->semantic(scp);
 
+    std::string msg = lhs_->has_error() ? lhs_->error_message() :
+                      rhs_->has_error() ? rhs_->error_message() : "";
+
+    if (!msg.empty()) {
+        error(msg);
+        return;
+    }
     if(rhs_->is_procedure_call()) {
         error("procedure calls can't be made in an expression");
     }
@@ -420,10 +462,19 @@ expression_ptr ConserveExpression::clone() const {
 }
 
 void ConserveExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
+
     lhs_->semantic(scp);
     rhs_->semantic(scp);
 
+    std::string msg = lhs_->has_error() ? lhs_->error_message() :
+                      rhs_->has_error() ? rhs_->error_message() : "";
+
+    if (!msg.empty()) {
+        error(msg);
+        return;
+    }
     if(rhs_->is_procedure_call()) {
         error("procedure calls can't be made in an expression");
     }
@@ -443,6 +494,7 @@ std::string CallExpression::to_string() const {
 }
 
 void CallExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
 
     // look up to see if symbol is defined
@@ -481,6 +533,9 @@ void CallExpression::semantic(scope_ptr scp) {
     // perform semantic analysis on the arguments
     for(auto& a : args_) {
         a->semantic(scp);
+        if(a->has_error()) {
+            error(a->error_message());
+        }
     }
 }
 
@@ -511,6 +566,7 @@ std::string ProcedureExpression::to_string() const {
 }
 
 void ProcedureExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
 
     // assert that the symbol is already visible in the global_symbols
@@ -525,6 +581,9 @@ void ProcedureExpression::semantic(scope_ptr scp) {
     // add the argumemts to the list of local variables
     for(auto& a : args_) {
         a->semantic(scope_);
+        if(a->has_error()) {
+            error(a->error_message());
+        }
     }
 
     // this loop could be used to then check the types of statements in the body
@@ -535,6 +594,9 @@ void ProcedureExpression::semantic(scope_ptr scp) {
 
     // perform semantic analysis for each expression in the body
     body_->semantic(scope_);
+    if(body_->has_error()) {
+        error(body_->error_message());
+    }
 
     // the symbol for this expression is itself
     symbol_ = scope_->find_global(name());
@@ -543,6 +605,7 @@ void ProcedureExpression::semantic(scope_ptr scp) {
 void ProcedureExpression::semantic(scope_type::symbol_map &global_symbols) {
     // create the scope for this procedure and run semantic pass on it
     scope_ptr scp = std::make_shared<scope_type>(global_symbols);
+    error_ = false;
     switch (kind_) {
     case procedureKind::derivative:
     case procedureKind::kinetic:
@@ -585,6 +648,8 @@ void APIMethod::semantic(scope_type::symbol_map &global_symbols) {
     // create the scope for this procedure, marking it as an API context,
     // and run semantic pass on it
     scope_ptr scp = std::make_shared<scope_type>(global_symbols);
+    error_ = false;
+
     scp->in_api_context(true);
     semantic(scp);
 }
@@ -618,14 +683,22 @@ void NetReceiveExpression::semantic(scope_type::symbol_map &global_symbols) {
 
     // create the scope for this procedure
     scope_ = std::make_shared<scope_type>(global_symbols);
+    error_ = false;
 
     // add the argumemts to the list of local variables
     for(auto& a : args_) {
         a->semantic(scope_);
+        if(a->has_error()) {
+            error(a->error_message());
+        }
     }
 
     // perform semantic analysis for each expression in the body
     body_->semantic(scope_);
+    if(body_->has_error()) {
+        error(body_->error_message());
+    }
+
     // this loop could be used to then check the types of statements in the body
     for(auto& e : *(body_->is_block())) {
         if(e->is_initial_block()) {
@@ -668,10 +741,14 @@ void FunctionExpression::semantic(scope_type::symbol_map &global_symbols) {
 
     // create the scope for this procedure
     scope_ = std::make_shared<scope_type>(global_symbols);
+    error_ = false;
 
     // add the argumemts to the list of local variables
     for(auto& a : args_) {
         a->semantic(scope_);
+        if(a->has_error()) {
+            error(a->error_message());
+        }
     }
 
     // Add a variable that has the same name as the function,
@@ -685,6 +762,9 @@ void FunctionExpression::semantic(scope_type::symbol_map &global_symbols) {
 
     // perform semantic analysis for each expression in the body
     body_->semantic(scope_);
+    if(body_->has_error()) {
+        error(body_->error_message());
+    }
     // this loop could be used to then check the types of statements in the body
     for(auto& e : *(body())) {
         if(e->is_initial_block()) error("INITIAL block not allowed inside FUNCTION definition");
@@ -699,10 +779,14 @@ void FunctionExpression::semantic(scope_type::symbol_map &global_symbols) {
   UnaryExpression
 *******************************************************************************/
 void UnaryExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
 
     expression_->semantic(scp);
-
+    if(expression_->has_error()) {
+        error(expression_->error_message());
+        return;
+    }
     if(expression_->is_procedure_call()) {
         error("a procedure call can't be part of an expression");
     }
@@ -720,10 +804,19 @@ expression_ptr UnaryExpression::clone() const {
   BinaryExpression
 *******************************************************************************/
 void BinaryExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
+
     lhs_->semantic(scp);
     rhs_->semantic(scp);
 
+    std::string msg = lhs_->has_error() ? lhs_->error_message() :
+                      rhs_->has_error() ? rhs_->error_message() : "";
+
+    if (!msg.empty()) {
+        error(msg);
+        return;
+    }
     if(rhs_->is_procedure_call() || lhs_->is_procedure_call()) {
         error("procedure calls can't be made in an expression");
     }
@@ -751,10 +844,20 @@ std::string BinaryExpression::to_string() const {
 *******************************************************************************/
 
 void AssignmentExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
+
     lhs_->semantic(scp);
     rhs_->semantic(scp);
 
+    std::string msg = lhs_->has_error() ? lhs_->error_message() :
+                      rhs_->has_error() ? rhs_->error_message() : "";
+
+    if (!msg.empty()) {
+        error(msg);
+        return;
+    }
+
     // only flag an lvalue error if there was no error in the lhs expression
     // this ensures that we don't print redundant error messages when trying
     // to write to an undeclared variable
@@ -771,6 +874,7 @@ void AssignmentExpression::semantic(scope_ptr scp) {
 *******************************************************************************/
 
 void SolveExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
 
     auto e = scp->find(name());
@@ -799,6 +903,7 @@ expression_ptr SolveExpression::clone() const {
 *******************************************************************************/
 
 void ConductanceExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
     // For now do nothing with the CONDUCTANCE statement, because it is not needed
     // to optimize conductance calculation.
@@ -831,9 +936,13 @@ std::string BlockExpression::to_string() const {
 }
 
 void BlockExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
     for(auto& e : statements_) {
         e->semantic(scope_);
+        if(e->has_error()) {
+            error(e->error_message());
+        }
     }
 }
 
@@ -862,18 +971,28 @@ std::string IfExpression::to_string() const {
 }
 
 void IfExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
 
     condition_->semantic(scp);
+    if(condition_->has_error()) {
+        error(condition()->error_message());
+    }
 
     if(!condition_->is_conditional()) {
         error("not a valid conditional expression");
     }
 
     true_branch_->semantic(scp);
+    if(true_branch_->has_error()) {
+        error(true_branch_->error_message());
+    }
 
     if(false_branch_) {
         false_branch_->semantic(scp);
+        if(false_branch_->has_error()) {
+            error(false_branch_->error_message());
+        }
     }
 }
 
@@ -899,6 +1018,7 @@ std::string PDiffExpression::to_string() const {
 }
 
 void PDiffExpression::semantic(scope_ptr scp) {
+    error_ = false;
     scope_ = scp;
 
     if (!var_->is_identifier()) {
@@ -906,7 +1026,13 @@ void PDiffExpression::semantic(scope_ptr scp) {
                       "an identifier, but instead %", yellow(var_->to_string())));
     }
     var_->semantic(scp);
+    if(var_->has_error()) {
+        error(var_->error_message());
+    }
     arg_->semantic(scp);
+    if(arg_->has_error()) {
+        error(arg_->error_message());
+    }
 }
 
 expression_ptr PDiffExpression::clone() const {
diff --git a/modcc/module.cpp b/modcc/module.cpp
index 9938a8636448262e0d2dfc16669ea27362f3fcd2..a4d0f68b93afccb75b5404fc8b4bbf04b4948061 100644
--- a/modcc/module.cpp
+++ b/modcc/module.cpp
@@ -757,6 +757,22 @@ int Module::semantic_func_proc() {
     //  -   generate local variable table for each function/procedure
     //  -   inlining function calls
     ////////////////////////////////////////////////////////////////////////////
+
+    // Before, make sure there are no errors
+    int errors = 0;
+    for(auto& e : symbols_) {
+        auto &s = e.second;
+        if(s->kind() == symbolKind::procedure || s->kind() == symbolKind::function) {
+            s->semantic(symbols_);
+            ErrorVisitor v(source_name());
+            s->accept(&v);
+            errors += v.num_errors();
+        }
+    }
+    if (errors > 0) {
+        return errors;
+    }
+
 #ifdef LOGGING
     std::cout << white("===================================\n");
         std::cout << cyan("        Function Inlining\n");
@@ -834,10 +850,11 @@ int Module::semantic_func_proc() {
         }
     }
 
-    int errors = 0;
+    errors = 0;
     for(auto& e : symbols_) {
         auto& s = e.second;
         if(s->kind() == symbolKind::procedure) {
+            s->semantic(symbols_);
             ErrorVisitor v(source_name());
             s->accept(&v);
             errors += v.num_errors();
diff --git a/test/unit-modcc/test_printers.cpp b/test/unit-modcc/test_printers.cpp
index 8e1635dcdc8e487666c628d4e219048f4dbf4ecb..3d8dcfa459bb15d934b3233016d149f3a4c98e1c 100644
--- a/test/unit-modcc/test_printers.cpp
+++ b/test/unit-modcc/test_printers.cpp
@@ -88,7 +88,10 @@ TEST(scalar_printer, statement) {
         ASSERT_TRUE(e);
 
         e->semantic(scope);
-
+        if(e->has_error()) {
+            std::cerr << e->error_message() << std::endl;
+            FAIL();
+        }
         {
             SCOPED_TRACE("CPrinter");
             std::stringstream out;