Home > Programming > What is wrong with my loop? Tips for solving errors in your loops

What is wrong with my loop? Tips for solving errors in your loops


Introduction

When your analysis involves a repetitive task, you can write a loop in Stata to more efficiently perform this task. For example, you may want to fit a regression model separately for different industries or regions. In Stata, you can loop over values and items in a list to efficiently perform these types of tasks. However, if you come across an error when working with a loop, it may seem tricky to find the source of the error. Below, I show you some essential tools to help you solve errors in your loops: set trace, capture, noisily, and the if command.

For example, we have data from the National Longitudinal Survey of Young Women (Center for Human Resource Research 1989). We are interested in modeling wage as a function of union membership, total work experience, and whether the individual is a college graduate. And we are interested in how this relationship differs across industries and time. We first obtain the levels of industry with levelsof and store these levels in a macro called industries. The date range for our data is 1968 to 1988. For each industry and year in this range, we fit the model and store the results with estimates store.

. use nlswork2
(National Longitudinal Survey of Young Women, 14-24 years old in 1968)
 
. levelsof ind_code, local(industries)
1 2 3 4 5 6 7 8 9 10 11 12

. foreach i of local industries {
  2.   forvalues y = 68(1)88 {
  3.        regress wage i.union ttl_exp i.collgrad if ind_code == `i'  
>              & year == `y' 
  4.        estimates store ind`i'_`y'
  5.   }
  6. }
no observations
r(2000);

We get an error, but it is not clear which year or industry is causing this error.

Trace execution of the loop

With set trace on, we can trace the execution of our code. Note that this will produce a lot of output; therefore, we use set tracedepth 1 to specify that only the first level of nested programs be traced. This means we’ll trace the execution of programs we call, such as regress, but not the programs that regress may call. By default, Stata would trace the execution of any program that is called; well, technically it would trace programs nested up to 32,000 levels. For example, if our loop calls regress, and regress calls summarize, by default Stata would also trace the execution of summarize and any programs summarize calls as well.

log using trace, replace
levelsof ind_code, local(industries)
set trace on
set tracedepth 1
foreach i of local industries {
   forvalues y = 68(1)88 {
      regress wage i.union ttl_exp i.collgrad if ind_code == `i' ///
         & year == `y'
      estimates store ind`i'_`y'
   }
}
set trace off
log close

Tracing the execution of our code generates a lot of output, so we create a log file to store the output. After the loops, we turn the tracing off and close the log file. When we run this code, the log file does not get closed because Stata errors out before the log close line gets executed; we’ll fix that issue below. For now, let’s examine the output.

. view trace.smcl

We see the following:

------------------------------------------------------------------------------
      name:  <unnamed>
       log:  /Users/Stata/Documents/trace.smcl
  log type:  smcl
 opened on:  24 Jun 2026, 22:33:14

. levelsof ind_code, local(industries)
1 2 3 4 5 6 7 8 9 10 11 12

. set trace on

. set tracedepth 1

. foreach i of local industries {
  2.   forvalues y = 68(1)88 {
  3.      regress wage i.union ttl_exp i.collgrad if ind_code == `i'  ///
>            & year == `y'
  4.      estimates store ind`i'_`y'
  5.   }
  6. }
- foreach i of local industries {
- forvalues y = 68(1)88 {
- regress wage i.union ttl_exp i.collgrad if ind_code == `i' & year == `y'
= regress wage i.union ttl_exp i.collgrad if ind_code == 1 & year == 68
------------------------------------------------------------- begin regress -----
(output omitted)
  = version 19.5, missing : _regress wage i.union ttl_exp i.collgrad 
> if ind_code == 1 & year == 68  [],
no observations
    }
------------------------------------------------------------- end regress -------
  estimates store ind`i'_`y'
  }
  }
r(2000);

Each line of code starts with a marker; a dash is displayed if a line was executed, and an equal sign is displayed if macros were expanded, to show the command that was executed. For example, the lines beginning with foreach, forvalues, and regress were executed. We see that the first model to be fit was for industry code 1 and year 68. We then see code from regress being traced. We can scroll to the bottom and see that this model was not fit because we didn’t have enough observations. Note that the line beginning with estimates store does not have a dash or equal sign; this is because the line was not executed: Stata errored out before it could execute this line. Now we know that year 68 for industry code 1 is problematic; but we would like to try fitting the regression model for other years and industry codes, which we do below.

Capture output and return code

Below, we add two prefixes to the regress command: capture and noisily. The capture prefix will capture any output, including errors; this allows us to loop through the other industry codes, even though our first industry-year combination resulted in an error. When we combine capture with noisily, we can view the output of the command while also capturing any errors. Additionally, if we fail to fit the model, the estimates store command would error out because it would fail to find the estimation results; therefore, we add the capture prefix for estimates store as well.

log using trace2, replace
levelsof ind_code, local(industries)
set trace on
set tracedepth 1
foreach i of local industries {
   forvalues y = 68(1)88 {
      capture noisily: regress wage i.union ttl_exp i.collgrad ///
         if ind_code == `i' & year == `y'
      capture: estimates store ind`i'_`y'
   }
}
set trace off
log close

When we run our code, we see the following:

-------------------------------------------------------------------------------
      name:  <unnamed>
       log:  /Users/Stata/Documents/trace2.smcl
  log type:  smcl
 opened on:  24 Jun 2026, 22:08:35

. levelsof ind_code, local(industries)
1 2 3 4 5 6 7 8 9 10 11 12

. set trace on

. set tracedepth 1

. foreach i of local industries {
2.   forvalues y = 68(1)88 {
3.      capture noisily: regress wage i.union ttl_exp i.collgrad  ///
>          if ind_code == `i' & year == `y'
4.      capture: estimates store ind`i'_`y'
5.   }
6.  }
- foreach i of local industries {
- forvalues y = 68(1)88 {
- capture noisily: regress wage i.union ttl_exp i.collgrad if ind_code == `i' 
> & year == `y'
= capture noisily: regress wage i.union ttl_exp i.collgrad if ind_code == 1 
> & year == 68
------------------------------------------------------------- begin regress -----
- version 9, missing
- local version : di "version " string(_caller()) ", missing :"
(output omitted)
= version 19.5, missing : _regress wage i.union ttl_exp i.collgrad 
> if ind_code == 12 & year == 88  [],
      Source |       SS           df       MS      Number of obs   =       157
-------------+----------------------------------   F(3, 153)       =     25.44
       Model |  749.180198         3  249.726733   Prob > F        =    0.0000
    Residual |  1501.69351       153  9.81499027   R-squared       =    0.3328
-------------+----------------------------------   Adj R-squared   =    0.3198
       Total |  2250.87371       156  14.4286776   Root MSE        =    3.1329

-------------------------------------------------------------------------------
         wage | Coefficient  Std. err.      t    P>|t|     [95% conf. interval]
--------------+----------------------------------------------------------------
        union |
Union member  |   1.773845   .5221935     3.40   0.001     .7422045    2.805486
      ttl_exp |   .3092888   .0594141     5.21   0.000     .1919109    .4266666
              |
     collgrad |
    Graduate  |   3.543574   .5787326     6.12   0.000     2.400235    4.686912
        _cons |   3.078143   .9150381     3.36   0.001     1.270403    4.885883
-------------------------------------------------------------------------------
(output omitted)
exit 198
}
----------------------------------------------------------- end estimates -------
- }
- }

. set trace off

. log close
name:  <unnamed>
log:  /Users/Stata/Documents/trace2.smcl
log type:  smcl
closed on:  24 Jun 2026, 22:08:36
-------------------------------------------------------------------------------

Our loop now runs to completion, even if we could not fit a model for all combinations of industry and year. In our log file, trace2.smcl, we can search for “no observations” to see which industry and year combinations we were not able to fit a model for. Additionally, we could use estimates dir to get a list of all the estimation results we collected.

. estimates dir

I leave that as an exercise for you to run on your own because this will produce a long list of results.

Execute code conditional on an error

Alternatively, you might also want a list of the industry and year combinations for which we could not fit a model. With your own data, you might not expect any errors, and identifying these combinations will allow you to investigate why those models weren’t fit. Below, we add a block that is executed when there is an error.

if _rc != 0 {
   display "error for industry `i' in year `y'"
   local nogood "`nogood' industry `i' in year `y',"
}

When a command is executed without any error, the return code (_rc) is zero. When the command results in an error, the return code is something other than zero. Therefore, the display and local commands will be executed when there is an error (if _rc != 0); this code block is conditional on _rc being equal to something other than zero. First, we identify the year and industry that result in error and display them; this way, we can easily search for “error” in the log file and identify those problematic values. Second, we store these problematic values in a macro called nogood. At first, the macro nogood will be empty; each time there is an error, we will add on to the contents. Once the loop concludes, we’ll list the contents of the macro; this way, we have a list of all the problematic values.

-------------------------------------------------------------------------------
name:  <unnamed>
log:  /Users/Stata/Documents/trace3.smcl
log type:  smcl
opened on:  24 Jun 2026, 22:39:58

. levelsof ind_code, local(industries)
1 2 3 4 5 6 7 8 9 10 11 12

. set trace on

. set tracedepth 1

. foreach i of local industries {
2.   forvalues y = 68(1)88 {
3.      capture noisily: regress wage i.union ttl_exp i.collgrad     ///
>          if ind_code == `i' & year == `y'
4.      if _rc != 0 {
5.         display "error for industry `i' in year `y'"              ///   
6.         local nogood "`nogood' industry `i' in year `y',"
7.         }
8.      capture: estimates store ind`i'_`y'
9.      macro list _nogood
10.     }
11. }
(output omitted)
----------------------------------------------------------- end estimates -------
- macro list _nogood
_nogood:         industry 1 in year 68, industry 1 in year 69, industry 1 in year 74,
                 industry 1 in year 75, industry 1 in year 76, industry 1 in year 79,
                 industry 1 in year 81, industry 1 in year 84, industry 1 in year 86,
(output omitted)
                 industry 12 in year 76, industry 12 in year 79, industry 12 in year 81,
                 industry 12 in year 84, industry 12 in year 86,
- }
- }

. set trace off
. log close
name:  <unnamed>
log:  /Users/Stata/Documents/trace3.smcl
log type:  smcl
closed on:  24 Jun 2026, 22:39:59
-------------------------------------------------------------------------------

For our data, this creates a long list of problematic years and industries. Therefore, you can choose whichever method works best for you—searching for “error” in your log file or creating a macro with the list of industries and years.

Execute code conditional on an expression

Now that we know that we do not have enough observations to fit our model for each industry and year, we show how to fit models conditional on having enough observations. For example, suppose we want to fit the model only if we have more than 500 observations. We first count the number of observations for each industry and year for which none of our variables are missing. The number of observations that satisfy the condition is stored in r(N); therefore, we fit our model conditional on the expression r(N) > 500.

estimates clear
levelsof ind_code, local(industries)
foreach i of local industries {
   forvalues y = 68(1)88 {
      quietly: count if ind_code == `i' & year == `y' ///
         & !missing(wage, collgrad, union, ttl_exp)
      if r(N) > 500 {
         regress wage i.union ttl_exp i.collgrad if ind_code == `i' & year == `y'
         estimates store ind`i'_`y'
      }
   }
}
estimates dir
estimates table ind*_*, stat(N)

When we run our code we see that only seven models were fit:

. estimates table ind*_*, stat(N)
---------------------------------------------------------------------------------------------------------
    Variable |  ind11_77     ind11_80     ind11_82     ind11_83     ind11_85     ind11_87     ind11_88
-------------+-------------------------------------------------------------------------------------------
       union |
Union mem..  |  .65417996   -.01906214    .24578505    .61643879     .4246801     .9367774    .51684473
             |
     ttl_exp |  .30374741    .24190665    .32396762    .23617784    .25427367    .27283157      .250586
             |
    collgrad |
   Graduate  |  2.1788221    2.7105716    2.2818487    2.3144089     3.151545     2.854899    3.1808656
             |
       _cons |  3.7257051    3.5762698    2.9726241    3.7179528    3.3201477    2.9833607    3.0227278
-------------+-------------------------------------------------------------------------------------------
           N |        680          570          691          648          750          806          723
---------------------------------------------------------------------------------------------------------

Summary

In this blog post, you learned how to trace the execution of your code, which allows you to quickly identify which commands in a loop resulted in an error. Tracing the execution allows you to easily see what macros get expanded to and thus allows you to see exactly what was issued in Stata. Additionally, you learned how to use the capture and noisily prefixes to run a loop to completion, capturing any errors but also displaying them for further investigation. Finally, you learned how to use the if command to run a block of code conditional on an expression and a return code; this allows you to filter out cases that are not of interest and to create a list of cases that require further investigation.

Reference

Center for Human Resource Research. 1989. National Longitudinal Survey of Labor Market Experience, Young Women 14–24 years of age in 1968. Columbus, OH: Ohio State University Press.

Cox, N. J. 2020. Speaking Stata: Loops, again and again. Stata Journal 20: 999–1015. https://doi.org/10.1177/1536867X20976340.

——. 2026. Speaking Stata: How to debug, part I. Stata Journal 26: 132–143. https://doi.org/10.1177/1536867X261425801.