15  Errors and conditions

R reports errors with a longjmp: Rf_error() never returns, abandoning your C function on the spot. That has two consequences you must design around. First, any cleanup your function owes — freeing R_Calloc() memory, closing files, restoring global state — will not run unless you arrange it in advance with R_UnwindProtect() or an external pointer finalizer. Second, in C++ the longjmp skips destructors, so RAII objects must live below an R_UnwindProtect() boundary that converts the longjmp into a C++ exception (see R_MakeUnwindCont()).

For catching errors from R code you call, rather than signalling your own, see the protected evaluation entry points in Evaluation.

15.1 Signalling

Follows WRE §6.2, Error signaling closely.

Rf_error() and Rf_warning() take printf-style format strings. If your message is a plain string that might contain %, pass it as Rf_error("%s", msg) so it isn’t interpreted as a format.

To signal an error with a custom class — so R code can catch it selectively with tryCatch(my_class = function(c) ...) — there is no dedicated entry point. Build a condition object (a named list with message and call fields, classed c("my_class", "error", "condition")) and signal it by evaluating a call to stop():

SEXP cls = PROTECT(Rf_allocVector(STRSXP, 3));
SET_STRING_ELT(cls, 0, Rf_mkChar("negative_value"));
SET_STRING_ELT(cls, 1, Rf_mkChar("error"));
SET_STRING_ELT(cls, 2, Rf_mkChar("condition"));

SEXP cond = PROTECT(Rf_mkNamed(VECSXP, (const char *[]){"message", "call", ""}));
SET_VECTOR_ELT(cond, 0, Rf_mkString("x must be non-negative"));
SET_VECTOR_ELT(cond, 1, R_NilValue);
Rf_classgets(cond, cls);

/* Never returns, so the UNPROTECT is never reached */
Rf_eval(Rf_lang2(Rf_install("stop"), cond), R_GlobalEnv);

The same pattern with warning() signals a classed warning.

15.1.1 Rf_error(), Rf_errorcall()

throws

Header: Rinternals.h
R equivalent: stop()

Signal an error, optionally including the call in the message.

void Rf_error(const char* format, ...);
void Rf_errorcall(SEXP call, const char* format, ...);

Use Rf_errorcall(R_NilValue, ...) to suppress display of the call.

15.1.2 Rf_warning(), Rf_warningcall(), Rf_warningcall_immediate()

throws

Header: R_ext/Error.h
R equivalent: warning()

Signal a warning, optionally including the call or displaying it immediately.

void Rf_warning(const char* format, ...);
void Rf_warningcall(SEXP call, const char*, ...);
void Rf_warningcall_immediate(SEXP call, const char*, ...);

15.1.3 error_return(), errorcall_return()

throws

Header: Rinternals.h

Eliminate compiler warnings about non-void functions that don’t return.

#define error_return(msg) { \
  Rf_error(msg); \
  return R_NilValue; \
}
#define errorcall_return(cl,msg) { \
  Rf_errorcall(cl, msg); \
  return R_NilValue; \
}

Rf_error will longjmp and so any code following will not be executed; however, most compilers do not detect this when providing warnings.

See also: Rf_error()

15.1.4 UNIMPLEMENTED()

throws

Header: R_ext/Error.h

Signal an error for an unimplemented operation.

[[noreturn]] void UNIMPLEMENTED(const char *s);

Signals “feature ‘s’ is not implemented”; never returns. Use UNIMPLEMENTED_TYPE() for a type-specific variant.

See also: Rf_error()

15.2 Condition handling and cleanup

Follows WRE §6.13, Condition handling and cleanup code closely.

These functions let C code do what tryCatch() and on.exit() do at the R level: run a body function with a condition handler or cleanup action installed. Reach for R_UnwindProtect() when you need guaranteed cleanup, and the R_tryCatch*() family when you need to inspect or recover from a condition object.

15.2.1 R_UnwindProtect()

needs protect throws

Header: Rinternals.h

Run a C function, guaranteeing a cleanup action runs on normal return and on longjmp.

SEXP R_UnwindProtect(SEXP (*fun)(void *data), void *data,
                     void (*clean)(void *cdata, Rboolean jump), void *cdata,
                     SEXP cont);
  • fun: function to run; its return value is passed through.
  • clean: cleanup function, called with jump = FALSE after a normal return and jump = TRUE before a non-local transfer of control resumes.
  • cont: continuation token from R_MakeUnwindCont(), or R_NilValue for plain C use.

Returns: The SEXP returned by fun.

This is the primary tool for making C (and C++) code longjmp-safe: free R_Calloc() memory, close files, and restore state in clean. With a continuation token, clean can throw a C++ exception to unwind the C++ stack and then call R_ContinueUnwind() to resume R’s longjmp.

See also: R_MakeUnwindCont(), R_ExecWithCleanup()

15.2.2 R_MakeUnwindCont(), R_ContinueUnwind()

needs protect throws

Header: Rinternals.h

Allocate a continuation token for C++ stack unwinding, and resume the unwind.

SEXP R_MakeUnwindCont(void);
NORET void R_ContinueUnwind(SEXP cont);

Returns: R_MakeUnwindCont() returns a SEXP continuation token for use with R_UnwindProtect(); R_ContinueUnwind() never returns.

PROTECT the token before passing it to R_UnwindProtect(). Only needed when C++ code sits between R and the cleanup handler; plain C code should pass R_NilValue as cont instead.

See also: R_UnwindProtect()

15.2.3 R_ExecWithCleanup()

needs protect throws

Header: Rinternals.h

Execute a C function in a protected context, with cleanup before teardown.

SEXP R_ExecWithCleanup(SEXP (*fun)(void *), void *data,
                       void (*cleanfun)(void *),
                       void *cleandata);
  • fun: C function to call after context setup. Passed *data.
  • cleanfun: C function to call before context teardown. Passed *data.

Returns: The SEXP returned by fun.

Older, simpler variant of R_UnwindProtect(); the cleanup function is not told whether the exit was a normal return or a longjmp.

See also: R_UnwindProtect()

15.2.4 R_tryCatchError(), R_withCallingErrorHandler()

needs protect throws

Header: Rinternals.h
R equivalent: tryCatch()

Call a C function with a handler installed for R error conditions.

SEXP R_tryCatchError(SEXP (*fun)(void *data), void *data,
                     SEXP (*hndlr)(SEXP cond, void *hdata), void *hdata);
SEXP R_withCallingErrorHandler(SEXP (*fun)(void *data), void *data,
                               SEXP (*hndlr)(SEXP cond, void *hdata), void *hdata);
  • hndlr: handler called with the condition object; its return value becomes the result.

Returns: The SEXP returned by fun, or the handler’s return value if an error is signalled.

R_tryCatchError() installs an exiting handler (like tryCatch(error = ...)); R_withCallingErrorHandler() installs a calling handler (like withCallingHandlers(error = ...)) and avoids calling back into R, so it is more efficient. R_tryCatchError() is implemented via R-level tryCatch() and has some overhead.

See also: R_tryCatch(), R_tryEval()

15.2.5 R_tryCatch()

needs protect throws

Header: Rinternals.h
R equivalent: tryCatch()

Call a C function with handlers for arbitrary condition classes and a cleanup action.

SEXP R_tryCatch(SEXP (*fun)(void *data), void *data,
                SEXP conds,
                SEXP (*hndlr)(SEXP cond, void *hdata), void *hdata,
                void (*clean)(void *cdata), void *cdata);
  • conds: condition classes to handle, as a character vector (STRSXP).

Returns: The SEXP returned by fun, or the handler’s return value if a handled condition is signalled.

NULL may be passed for fun or clean if condition handling or cleanup is not needed. Implemented via R-level tryCatch(), so it has some overhead.

See also: R_tryCatchError(), R_UnwindProtect()

15.3 Interrupts

Follows WRE §6.14, Allowing interrupts closely.

15.3.1 R_CheckUserInterrupt()

throws

Header: R_ext/Utils.h

Check for a pending user interrupt, signalling an error if one occurred.

void R_CheckUserInterrupt(void);

Call periodically from long-running loops; R cannot interrupt compiled code that never checks. On interrupt it longjmps, so the same cleanup obligations as for Rf_error() apply.

15.3.2 Rf_onintr()

throws

Header: Rinterface.h

R’s default response to a user interrupt.

void Rf_onintr(void);

Jumps back to the top level, discarding the current computation. Intended for front-ends and graphics devices; package code should almost always use R_CheckUserInterrupt() instead.

See also: R_CheckUserInterrupt()

15.4 C stack checking

Follows WRE §6.15, C stack checking closely.

15.4.1 R_CheckStack(), R_CheckStack2()

throws

Header: R_ext/Utils.h

Signal an error if the C stack is (nearly) exhausted.

void R_CheckStack(void);
void R_CheckStack2(R_SIZE_T extra);
  • extra: R_CheckStack2() errors when fewer than extra bytes remain.

Call before deep C recursion; better still, avoid deep recursion or write it tail-recursively so the compiler can optimize it away. Stack checking is not available on all platforms.