Lodash _.attempt() Method

Beginner
⏱️ 8 min read
📚 Updated: Jul 2026
🎯 6 Examples + 3 Try It
Util utilities

What You’ll Learn

By the end of this tutorial, you’ll invoke risky functions safely with Lodash’s _.attempt() and handle failures using _.isError()—without scattering try/catch blocks everywhere.

01

Core Syntax

Call _.attempt(func, ...args) and inspect the return value.

02

Value or Error

Success returns the function result; failure returns the caught Error.

03

_.isError()

Distinguish errors from normal return values in one check.

04

Pass Arguments

Forward parameters after the function reference.

05

Functional Style

Use inside _.map() for batch safe transforms.

06

When Not to Use

Know when native try/catch or async patterns fit better.

What Is _.attempt()?

_.attempt() is a Lodash util helper that calls a function inside a try/catch and returns either the function’s return value or the caught error object. Instead of wrapping every risky call in a statement block, you get a single expression you can assign, pass to another function, or map over an array.

💡
Beginner tip

Think of _.attempt(JSON.parse, text) as “try to parse this string; if it fails, give me the Error object instead of crashing.” Then use _.isError(result) to branch.

This pattern shines when a failure is expected and recoverable—invalid user input, optional plugin callbacks, or per-row data transforms where one bad row should not stop the batch.

📝 Syntax

The signature accepts a function plus optional arguments forwarded to that function:

javascript
_.attempt(func, [args])

Syntax Rules

  • func — the function to invoke (not the result of calling it).
  • args — optional values passed to func after the first argument.
  • Return value — the function’s return value on success, or the caught error on failure.
  • Error check — use _.isError(value) to detect failure results.
  • Synchronous only_.attempt() does not await Promises; use try/catch with async/await for async work.
javascript
import attempt from "lodash/attempt";
import isError from "lodash/isError";

function riskyOperation() {
  throw new Error("Something went wrong");
}

const result = attempt(riskyOperation);

if (isError(result)) {
  console.error("An error occurred:", result.message);
} else {
  console.log("Operation result:", result);
}

⚡ Quick Reference

TaskCode patternResult
Call with no args_.attempt(fn)Return value or Error
Call with arguments_.attempt(parseInt, "42", 10)Forwards args to fn
Check for failure_.isError(result)true when result is an Error
Safe JSON parse_.attempt(JSON.parse, str)Object or SyntaxError
Map over items_.map(items, i => _.attempt(fn, i))Array of values/errors
Native equivalenttry { fn() } catch (e) { e }Statement-based
Throws?
No

Returns the Error

Async
No

Sync functions only

Pair with
_.isError

Detect failures

Category
Util

General helpers

🧰 Parameters

Every argument to _.attempt() and what it controls:

func Required

The function to invoke. Pass the function reference itself—do not call it first unless you intend to pass its return value (usually wrong for attempt).

_.attempt(JSON.parse, text)
args Optional

Zero or more arguments forwarded to func. Lodash applies them with func.apply(undefined, args).

_.attempt(parseInt, "ff", 16)
return value Value | Error

On success, whatever func returned (including undefined). On failure, the caught error object—typically an Error instance.

const r = _.attempt(fn)
_.isError() Companion

Lodash helper that returns true for Error-like objects. Use it immediately after _.attempt() to branch between success and failure paths.

if (_.isError(r)) { ... }

_.attempt() never re-throws the error—it always returns it. Your calling code decides whether to log, fallback, or propagate manually.

Examples Gallery

Practical _.attempt() patterns with copy-ready code, sample output, and interactive Try It Yourself labs.

📚 Getting Started

Catch thrown errors as return values and handle them with _.isError().

Example 1 — Handle a throwing function

When riskyOperation throws, _.attempt() returns the Error instead of crashing your script.

javascript
function riskyOperation() {
  throw new Error("Something went wrong");
}

const result = _.attempt(riskyOperation);

if (_.isError(result)) {
  console.error("An error occurred:", result.message);
} else {
  console.log("Operation result:", result);
}
// -> "An error occurred: Something went wrong"
Try It Yourself

How It Works

Lodash wraps the call in try/catch internally. The thrown Error becomes the return value, so execution continues in the same function scope.

Example 2 — Successful return path

When the function completes normally, you get its return value and _.isError() is false.

javascript
function add(a, b) {
  return a + b;
}

const result = _.attempt(add, 10, 32);

if (_.isError(result)) {
  console.error("Failed:", result.message);
} else {
  console.log("Sum:", result);
}
// -> "Sum: 42"

How It Works

Extra arguments after add are forwarded as add(10, 32). No exception means the numeric result passes through unchanged.

📈 Practical Patterns

Defensive parsing, argument forwarding, and batch transforms in collections.

Example 3 — Forward arguments safely

Pass the target function plus its parameters—useful for built-ins like parseInt that may throw or return unexpected values.

javascript
const good = _.attempt(parseInt, "42", 10);
const bad  = _.attempt(parseInt, "hello", 10);

console.log("good:", good);   // 42
console.log("bad:", bad);     // NaN (parseInt does not throw)
console.log("isError(good):", _.isError(good)); // false

How It Works

_.attempt() only catches thrown exceptions. Functions that return sentinel values like NaN still count as success—you may need extra validation after the call.

Example 4 — Safe JSON.parse on user input

Invalid JSON throws a SyntaxError. Wrap parsing with _.attempt() and fall back to a default object.

javascript
function parseConfig(raw) {
  const parsed = _.attempt(JSON.parse, raw);
  if (_.isError(parsed)) {
    return { theme: "light", lang: "en" };
  }
  return parsed;
}

console.log(parseConfig('{"theme":"dark"}'));
// -> { theme: "dark" }

console.log(parseConfig("{ not valid json"));
// -> { theme: "light", lang: "en" }
Try It Yourself

How It Works

JSON.parse throws on malformed input. _.attempt() converts that throw into a return value you can handle with a simple fallback—ideal for localStorage or query-string config strings.

Example 5 — Map over items without stopping the batch

Transform each row safely; collect errors separately so one bad record does not abort the loop.

javascript
function parseRow(row) {
  if (!row.payload) {
    throw new Error("Missing payload on row " + row.id);
  }
  return JSON.parse(row.payload);
}

const rows = [
  { id: 1, payload: '{"ok":true}' },
  { id: 2, payload: null },
  { id: 3, payload: '{"count":5}' }
];

const results = _.map(rows, function (row) {
  return _.attempt(parseRow, row);
});

const ok = [];
const failed = [];

_.forEach(results, function (result, index) {
  if (_.isError(result)) {
    failed.push({ id: rows[index].id, error: result.message });
  } else {
    ok.push({ id: rows[index].id, data: result });
  }
});

console.log("ok:", ok);
console.log("failed:", failed);
Try It Yourself

How It Works

Each _.attempt(parseRow, row) returns either parsed data or an Error. Partition the array afterward—no nested try/catch inside the callback.

🚀 Beyond the Basics

Compare with native error handling and know when each approach fits.

Example 6 — Native try/catch equivalent

The same logic with a statement block—choose whichever reads clearer in context.

javascript
function safeParse(raw) {
  // Lodash style (expression)
  const lodashResult = _.attempt(JSON.parse, raw);
  if (_.isError(lodashResult)) return null;
  return lodashResult;
}

function safeParseNative(raw) {
  // Native style (statement)
  try {
    return JSON.parse(raw);
  } catch (err) {
    return null;
  }
}

console.log(safeParse('{"a":1}'));        // { a: 1 }
console.log(safeParseNative('{"a":1}')); // { a: 1 }

When to prefer Lodash

Use _.attempt() inside higher-order functions where a try/catch block would require extracting a helper. For straightforward async I/O, prefer async/await with try/catch instead.

🧠 How _.attempt() Works

1

Receive func + args

Lodash collects the function reference and any trailing arguments.

Input
2

Try invoke

Calls func.apply(undefined, args) inside an internal try block.

Execute
3

Catch or return

On success, returns the function result. On throw, returns the caught value (usually an Error).

Result
=

No re-throw

Your code inspects the return value with _.isError() and decides the next step.

📝 Notes

  • _.attempt() is for synchronous functions—it does not await Promises returned by async functions.
  • Always pair with _.isError() when the function might throw—do not assume every failure is an Error instance.
  • Functions that signal failure via return values (like NaN) still count as success unless they throw.
  • Pass the function reference, not fn(), unless you deliberately want to attempt whatever fn() returned.
  • For network or file I/O, use async patterns; wrapping fetch() in _.attempt() only catches synchronous setup errors, not HTTP failures.
  • Related util helpers: _.over() runs multiple functions; _.cond() picks branches by predicate.

Conclusion

_.attempt() turns thrown exceptions into return values so you can handle errors as data. Combined with _.isError(), it keeps functional pipelines and collection transforms readable when some inputs are expected to fail.

Reach for native try/catch or async/await when control flow is linear and asynchronous. Use _.attempt() when a single expression fits better— especially inside _.map() over heterogeneous data.

💡 Best Practices

✅ Do

  • Check results with _.isError() immediately after every attempt
  • Use for small, isolated throws like JSON.parse on untrusted strings
  • Partition success and failure arrays after mapping with attempt
  • Throw proper Error objects in your own functions for clearer messages
  • Provide sensible fallbacks when errors are expected (defaults, null, empty arrays)

❌ Don’t

  • Wrap async functions expecting Promise rejection handling
  • Assume HTTP errors from fetch—failed responses do not throw by default
  • Replace all try/catch blocks when a statement reads clearer
  • Ignore non-Error thrown values—validate with _.isError() first
  • Call riskyFn() instead of passing riskyFn unless intentional

Key Takeaways

Knowledge Unlocked

Five things to remember about _.attempt()

Use these points whenever you need safe synchronous function calls.

5
Core concepts
🔍 02

isError

Detect failure results.

Pattern
📄 03

JSON.parse

Classic use case.

Practical
📊 04

Map batch

One bad row OK.

Scale
05

Sync only

Use async/await elsewhere.

Limit

❓ Frequently Asked Questions

_.attempt() calls a function inside an internal try/catch. If the call succeeds, it returns the function's return value. If the function throws, it returns the caught error object instead of propagating the exception.
try/catch uses control flow with a block statement. _.attempt() returns either a value or an Error in one expression, which is convenient inside _.map(), _.flow(), or functional pipelines where a statement block would be awkward.
Use _.isError(result). When true, result is the caught Error and you can read result.message. When false, result is the normal return value from your function.
Yes. Arguments after the function are forwarded: _.attempt(parseInt, '42', 10) calls parseInt('42', 10). This works the same as func.apply(undefined, args).
Use it for small, isolated risky calls—JSON.parse on user input, optional callbacks, or per-item transforms in a collection—where you want a value-or-error result without nested try/catch blocks.
Lodash catches any thrown value. _.isError() returns true only for Error-like objects. If someone throws a string or number, handle that with typeof checks or prefer throwing proper Error instances in your own code.
Did you know?

_.attempt() is the expression-oriented cousin of try/catch. Lodash also ships _.isError() in the same util category—it checks for Error-like objects so you can branch without instanceof Error quirks across iframes or realms.

Practice _.attempt() in the Live Editor

Open the Try It editor, run the examples, and experiment with your own risky functions.

Open Try It editor →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

6 people found this page helpful