Early return after errors

When handling errors in asynchronous functions, always return immediately after invoking a callback with an error to prevent subsequent code execution. This avoids the risk of calling callbacks multiple times or continuing execution paths that assume success.

copy reviewer prompt

Prompt

Reviewer Prompt

When handling errors in asynchronous functions, always return immediately after invoking a callback with an error to prevent subsequent code execution. This avoids the risk of calling callbacks multiple times or continuing execution paths that assume success.

Bad pattern:

function loadViaCredentialProcess(profile, callback) {
  proc.exec(profile['credential_process'], function(err, stdOut, stdErr) {
    if (err) {
      callback(err, null);
    }
    // Problem: execution continues even after error callback
    try {
      var credData = JSON.parse(stdOut);
      // More processing that might fail or call callback again
      callback(null, credData);
    } catch(e) {
      callback(e);
    }
  });
}

Good pattern:

function loadViaCredentialProcess(profile, callback) {
  proc.exec(profile['credential_process'], function(err, stdOut, stdErr) {
    if (err) {
      return callback(err, null); // Return immediately after error callback
    }
    try {
      var credData = JSON.parse(stdOut);
      // More processing that only happens on success path
      callback(null, credData);
    } catch(e) {
      return callback(e); // Return after error in try/catch as well
    }
  });
}

For functions that can be called synchronously or asynchronously:

function createPresignedPost(params, callback) {
  if (typeof params === 'function' && callback === undefined) {
    callback = params;
    params = null;
  }
  
  // Check for errors first, return early
  if (!this.config.credentials) {
    var error = new Error('No credentials');
    if (callback) {
      return callback(error);
    }
    throw error; // Throw for synchronous callers
  }
  
  // Success path only executes if no errors were found
  var result = this.finalizePost();
  return callback ? callback(null, result) : result;
}

This pattern creates clear separation between error and success paths, making code more maintainable and preventing hard-to-debug issues caused by multiple callback invocations or unexpected execution after errors.

Source discussions