---
title: "RetryOptions"
description: "RetryOptions"
url: "https://www.contentstack.com/docs/developers/sdks/content-delivery-sdk/java/reference/retryoptions"
product: "Contentstack"
doc_type: "guide"
audience:
  - developers
  - admins
version: "current"
last_updated: "2026-11-13"
---

# RetryOptions

## RetryOptions

RetryOptions sets the retry behavior of the Java Delivery SDK for a request that fails with a transient HTTP status or a network error. Use this class to

*   change the number of retries and the wait between them
*   choose a backoff strategy, or supply your own delay calculation
*   choose which HTTP status codes trigger a retry
*   turn retries off

Create an instance with new RetryOptions(), which starts with the defaults in the Class-Level Properties table below. Each setter returns the same instance, so you can chain them. Pass the instance to the [setRetryOptions](/docs/developers/sdks/content-delivery-sdk/java/reference/config#config-setretryoptions) method of Config before you create the stack.

## getBackoffStrategy

getBackoffStrategy returns the backoff strategy value stored on the options.

```
Returns: BackoffStrategy. The configured backoff strategy.
ValidationThere is no client-side validation.getBackoffStrategy does not throw.BehaviorThe default is EXPONENTIAL.setCustomBackoffStrategy sets the value to CUSTOM.The value does not always name the rule in use. After setCustomBackoffStrategy, the custom calculation decides every wait, even when a later setBackoffStrategy call changes this value. Check hasCustomBackoff as well.ExampleBasic usage: read and compare the strategyimport com.contentstack.sdk.RetryOptions;

public class ReadBackoffStrategy {
    public static void main(String[] args) {
        RetryOptions retryOptions = new RetryOptions();
        System.out.println(retryOptions.getBackoffStrategy()); // EXPONENTIAL, the default

        retryOptions.setBackoffStrategy(RetryOptions.BackoffStrategy.FIXED);
        if (retryOptions.getBackoffStrategy() == RetryOptions.BackoffStrategy.FIXED) {
            System.out.println("Every retry waits " + retryOptions.getRetryDelay() + " ms");
        }
    }
}
```

## getCustomBackoffStrategy

getCustomBackoffStrategy returns the wait calculation that [setCustomBackoffStrategy](/docs/developers/sdks/content-delivery-sdk/java/reference/retryoptions#retryoptions-setcustombackoffstrategy) stored.

```
Returns: CustomBackoffStrategy. The custom backoff strategy, or null.
ValidationThere is no client-side validation.getCustomBackoffStrategy does not throw.BehaviorReturns null until you call setCustomBackoffStrategy, which is the default state. Call hasCustomBackoff to check first.The returned object is the one you passed, so you can call its calculateDelay method to preview the waits.ExampleBasic usage: preview the waits of a custom strategyimport com.contentstack.sdk.CustomBackoffStrategy;
import com.contentstack.sdk.RetryOptions;

public class PreviewCustomDelays {
    public static void main(String[] args) {
        RetryOptions retryOptions = new RetryOptions()
                .setCustomBackoffStrategy((attempt, statusCode, exception) -> 1500L * (attempt + 1));

        CustomBackoffStrategy strategy = retryOptions.getCustomBackoffStrategy();
        if (strategy != null) {
            for (int attempt = 0; attempt < retryOptions.getRetryLimit(); attempt++) {
                // Preview the wait for a 503 response on each retry
                System.out.println("Retry " + attempt + ": " + strategy.calculateDelay(attempt, 503, null) + " ms");
            }
        }
    }
}
```

## getRetryDelay

getRetryDelay returns the base wait, in milliseconds, that the backoff strategy uses.

```
Returns: long. The base wait in milliseconds.
ValidationThere is no client-side validation.getRetryDelay does not throw.BehaviorThe default is 1000.The value is the base, not the actual wait. The strategy turns it into each wait. With the default EXPONENTIAL strategy, the waits are 1, 2, and 4 times the base delay. The strategy table on the RetryOptions class page lists every strategy.The SDK ignores this value once the options hold a custom backoff strategy. Check with the hasCustomBackoff method.ExampleBasic usage: read the base delayimport com.contentstack.sdk.RetryOptions;

public class ReadRetryDelay {
    public static void main(String[] args) {
        RetryOptions retryOptions = new RetryOptions().setRetryDelay(250);
        System.out.println(retryOptions.getRetryDelay());       // 250
        System.out.println(new RetryOptions().getRetryDelay()); // 1000, the default
    }
}
```

## getRetryLimit

getRetryLimit returns the maximum number of times the SDK retries a failed request after the first attempt.

```
Returns: int. The maximum number of retries.
ValidationThere is no client-side validation.getRetryLimit does not throw.BehaviorThe default is 3. The SDK then sends up to four attempts in total.The value is always from 0 to 10, because the setRetryLimit method rejects anything else.ExampleBasic usage: read the retry limitimport com.contentstack.sdk.RetryOptions;

public class ReadRetryLimit {
    public static void main(String[] args) {
        RetryOptions retryOptions = new RetryOptions().setRetryLimit(5);
        System.out.println(retryOptions.getRetryLimit());       // 5
        System.out.println(new RetryOptions().getRetryLimit()); // 3, the default
    }
}
```

## getRetryableStatusCodes

getRetryableStatusCodes returns the HTTP status codes that make the SDK retry a request.

```
Returns: int[]. A copy of the retryable status codes.
ValidationThere is no client-side validation.getRetryableStatusCodes does not throw.BehaviorThe default list is 408, 429, 502, 503, and 504.Each call returns a new copy. Changing the returned array does not change the options. Use the setRetryableStatusCodes method instead.An empty array means the SDK retries network errors only.ExampleBasic usage: read the status codesimport com.contentstack.sdk.RetryOptions;

import java.util.Arrays;

public class ReadStatusCodes {
    public static void main(String[] args) {
        RetryOptions retryOptions = new RetryOptions();
        int[] codes = retryOptions.getRetryableStatusCodes();
        System.out.println(Arrays.toString(codes)); // [408, 429, 502, 503, 504]

        codes[0] = 500; // changes the copy only
        System.out.println(Arrays.toString(retryOptions.getRetryableStatusCodes())); // [408, 429, 502, 503, 504]
    }
}
```

## hasCustomBackoff

hasCustomBackoff reports whether a custom calculation decides the wait before each retry.

```
Returns: boolean. True when the options hold a custom strategy.
ValidationThere is no client-side validation.hasCustomBackoff does not throw.BehaviorReturns false by default.Returns true from the first setCustomBackoffStrategy call onward. No method sets it back to false.When it returns true, the SDK ignores getRetryDelay and getBackoffStrategy when it calculates a wait.ExampleBasic usage: check for a custom strategyimport com.contentstack.sdk.RetryOptions;

public class CheckCustomBackoff {
    public static void main(String[] args) {
        RetryOptions retryOptions = new RetryOptions();
        System.out.println(retryOptions.hasCustomBackoff()); // false

        retryOptions.setCustomBackoffStrategy((attempt, statusCode, exception) -> 3000L);
        System.out.println(retryOptions.hasCustomBackoff()); // true
    }
}
```

## isRetryEnabled

isRetryEnabled reports whether the SDK's retry step runs.

```
Returns: boolean. True when the retry step runs.
ValidationThere is no client-side validation.isRetryEnabled does not throw.BehaviorThe default is true.The value reflects these options only. A stack created while the value was false has no retry step, so it does not retry even after the value changes to true. See the setRetryEnabled method.true with a retry limit of 0 still sends one attempt.ExampleBasic usage: check whether retries are onimport com.contentstack.sdk.RetryOptions;

public class ReadRetryEnabled {
    public static void main(String[] args) {
        RetryOptions retryOptions = new RetryOptions();
        System.out.println(retryOptions.isRetryEnabled()); // true, the default

        retryOptions.setRetryEnabled(false);
        System.out.println(retryOptions.isRetryEnabled()); // false
    }
}
```

## setBackoffStrategy

setBackoffStrategy chooses the rule that grows the wait from one retry to the next.

```
Returns: RetryOptions. The same retry options, for chaining.
ValidationThrows NullPointerException if strategy is null, because the SDK needs a rule to calculate each wait.The exception comes from setBackoffStrategy itself, at the call. The options keep their previous strategy.BehaviorThe default strategy is EXPONENTIAL.The four values calculate the wait before retry n (starting at 0) from the base delay that setRetryDelay sets, as follows:FIXED waits the base delay every time.LINEAR waits the base delay × (n + 1).EXPONENTIAL waits the base delay × 2 raised to the power n.CUSTOM waits the value from your CustomBackoffStrategy. Without one, it waits the base delay, the same as FIXED.Selecting CUSTOM here does not supply a calculation. Use the setCustomBackoffStrategy method, which also selects CUSTOM.After setCustomBackoffStrategy, a later setBackoffStrategy call changes the value that getBackoffStrategy returns, but the custom calculation still decides every wait.ExampleBasic usage: use a linear backoffimport com.contentstack.sdk.Config;
import com.contentstack.sdk.Contentstack;
import com.contentstack.sdk.RetryOptions;
import com.contentstack.sdk.Stack;

public class LinearBackoff {
    public static void main(String[] args) throws IllegalAccessException {
        RetryOptions retryOptions = new RetryOptions()
                .setRetryDelay(1000)
                .setBackoffStrategy(RetryOptions.BackoffStrategy.LINEAR); // waits 1000, 2000, 3000 ms

        // Attach the options before creating the stack
        Config config = new Config();
        config.setRetryOptions(retryOptions);
        Stack stack = Contentstack.stack("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>", config);

        System.out.println(config.getRetryOptions().getBackoffStrategy()); // LINEAR
    }
}Edge case: a custom strategy keeps deciding the wait after a strategy changeimport com.contentstack.sdk.RetryOptions;

public class CustomStrategyPriority {
    public static void main(String[] args) {
        RetryOptions retryOptions = new RetryOptions()
                .setCustomBackoffStrategy((attempt, statusCode, exception) -> 2000L)
                .setBackoffStrategy(RetryOptions.BackoffStrategy.FIXED);

        System.out.println(retryOptions.getBackoffStrategy()); // FIXED
        System.out.println(retryOptions.hasCustomBackoff());   // true, so every wait is still 2000 ms
    }
}
```

Rule that turns the base delay into each wait.

## setCustomBackoffStrategy

setCustomBackoffStrategy supplies your own calculation for the wait before each retry, and selects the CUSTOM strategy.

```
Returns: RetryOptions. The same retry options, for chaining.
ValidationThrows NullPointerException if customStrategy is null, because the SDK calls it before every retry.The exception comes from setCustomBackoffStrategy itself, at the call. The options keep their previous strategy.BehaviorCustomBackoffStrategy is a functional interface, so a lambda works. Its one method is long calculateDelay(int attempt, int statusCode, IOException exception). The RetryOptions class page describes each argument.The SDK calls calculateDelay before every retry, for a status retry and a network error retry alike, and waits for the number of milliseconds it returns.For a status retry, statusCode holds the HTTP status and exception is null.For a network error, statusCode is -1 and exception holds the java.io.IOException.Return 0 or a positive number. A negative value makes Thread.sleep throw IllegalArgumentException.The custom calculation replaces the base delay and the strategy value. A later setBackoffStrategy call does not switch it off.No method removes a custom strategy, because setCustomBackoffStrategy rejects null. Create a new RetryOptions to go back to a built-in strategy.The SDK still applies the retry limit and the status list. calculateDelay sets only the wait.ExampleBasic usage: wait longer after a rate limitimport com.contentstack.sdk.Config;
import com.contentstack.sdk.Contentstack;
import com.contentstack.sdk.RetryOptions;
import com.contentstack.sdk.Stack;

public class RateLimitBackoff {
    public static void main(String[] args) throws IllegalAccessException {
        RetryOptions retryOptions = new RetryOptions()
                .setCustomBackoffStrategy((attempt, statusCode, exception) -> {
                    if (statusCode == 429) {
                        return 5000L; // rate limited: wait five seconds
                    }
                    return 1000L * (attempt + 1); // other failures: 1000, 2000, 3000 ms
                });

        // Attach the options before creating the stack
        Config config = new Config();
        config.setRetryOptions(retryOptions);
        Stack stack = Contentstack.stack("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>", config);

        System.out.println(config.getRetryOptions().getBackoffStrategy()); // CUSTOM
    }
}Edge case: log network errors from an anonymous classimport com.contentstack.sdk.CustomBackoffStrategy;
import com.contentstack.sdk.RetryOptions;

import java.io.IOException;

public class LogNetworkRetries {
    public static void main(String[] args) {
        RetryOptions retryOptions = new RetryOptions().setCustomBackoffStrategy(new CustomBackoffStrategy() {
            @Override
            public long calculateDelay(int attempt, int statusCode, IOException exception) {
                if (exception != null) {
                    // statusCode is -1 for a network error
                    System.out.println("Retry " + attempt + " after: " + exception.getMessage());
                }
                return 2000L;
            }
        });

        System.out.println(retryOptions.hasCustomBackoff()); // true
    }
}Error handling: reject a null strategyimport com.contentstack.sdk.RetryOptions;

public class RejectNullStrategy {
    public static void main(String[] args) {
        RetryOptions retryOptions = new RetryOptions();
        try {
            retryOptions.setCustomBackoffStrategy(null);
        } catch (NullPointerException e) {
            // Thrown at the call when the strategy is null
            System.out.println("Strategy required: " + e.getMessage());
        }
        System.out.println(retryOptions.hasCustomBackoff()); // false
    }
}
```

Calculates the wait in milliseconds before each retry.

## setRetryDelay

setRetryDelay sets the base wait, in milliseconds, that the backoff strategy uses to calculate the wait before each retry.

```
Returns: RetryOptions. The same retry options, for chaining.
ValidationThrows IllegalArgumentException if delayMs is below 0, because a wait cannot be negative.The exception comes from setRetryDelay itself, at the call. The options keep their previous delay.There is no upper limit. A large value blocks the calling thread for that long before each retry.BehaviorThe default base delay is 1000 ms.The strategy turns the base delay into the actual wait. With the default EXPONENTIAL strategy, a base of 500 gives waits of 500, 1000, and 2000 ms. See the strategy table on the RetryOptions class page.0 sends the retries back to back with no wait.A custom backoff strategy replaces this value. The SDK ignores the base delay after you call setCustomBackoffStrategy.An int argument such as 500 widens to long, so it needs no L suffix.ExampleBasic usage: wait half a second before the first retryimport com.contentstack.sdk.Config;
import com.contentstack.sdk.Contentstack;
import com.contentstack.sdk.RetryOptions;
import com.contentstack.sdk.Stack;

public class ShortRetryDelay {
    public static void main(String[] args) throws IllegalAccessException {
        RetryOptions retryOptions = new RetryOptions().setRetryDelay(500);

        // Attach the options before creating the stack
        Config config = new Config();
        config.setRetryOptions(retryOptions);
        Stack stack = Contentstack.stack("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>", config);

        System.out.println(config.getRetryOptions().getRetryDelay()); // 500
    }
}Error handling: reject a negative delayimport com.contentstack.sdk.RetryOptions;

public class RejectRetryDelay {
    public static void main(String[] args) {
        RetryOptions retryOptions = new RetryOptions();
        try {
            retryOptions.setRetryDelay(-1);
        } catch (IllegalArgumentException e) {
            // Thrown at the call for a delay below 0
            System.out.println("Invalid retry delay: " + e.getMessage());
        }
        System.out.println(retryOptions.getRetryDelay()); // 1000, the default, unchanged
    }
}
```

Base wait between attempts, in milliseconds.

## setRetryEnabled

setRetryEnabled turns the SDK's retry step on or off.

```
Returns: RetryOptions. The same retry options, for chaining.
ValidationThere is no client-side validation.BehaviorThe default is true.false makes the SDK send one attempt per request, whatever the limit, delay, strategy, and status list hold. A failure then goes to the callback at once.IMPORTANT: Set the value before you call Contentstack.stack(...). The SDK adds the retry step only when this value is true at stack creation. Calling setRetryEnabled(true) after creating a stack with the value false has no effect, and nothing reports it.Calling setRetryEnabled(false) on the attached options after stack creation stops retries from the next request.Passing null to the setRetryOptions method of Config also turns retries off.ExampleBasic usage: turn retries offimport com.contentstack.sdk.Config;
import com.contentstack.sdk.Contentstack;
import com.contentstack.sdk.RetryOptions;
import com.contentstack.sdk.Stack;

public class DisableRetries {
    public static void main(String[] args) throws IllegalAccessException {
        RetryOptions retryOptions = new RetryOptions().setRetryEnabled(false);

        // Attach the options before creating the stack
        Config config = new Config();
        config.setRetryOptions(retryOptions);
        Stack stack = Contentstack.stack("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>", config);

        System.out.println(config.getRetryOptions().isRetryEnabled()); // false
    }
}
```

true retries failed requests, false sends **one attempt**.

## setRetryLimit

setRetryLimit sets the maximum number of times the SDK retries a failed request after the first attempt.

```
Returns: RetryOptions. The same retry options, for chaining.
ValidationThrows IllegalArgumentException if limit is below 0, because a retry count cannot be negative.Throws IllegalArgumentException if limit is above 10. The cap keeps one failing request from blocking the calling thread indefinitely.The exception comes from setRetryLimit itself, at the call. The options keep their previous limit.BehaviorThe limit counts retries, not attempts. The SDK sends up to limit + 1 attempts, so the default of 3 sends up to four attempts.0 turns retries off while leaving the retry step in place. The SDK then sends one attempt.The total wait grows with the limit. With the default EXPONENTIAL strategy and a limit of 10, the waits add up to 1023 times the base delay. That is about 17 minutes with the default 1000 ms delay.Read the value back with the getRetryLimit method.ExampleBasic usage: allow five retriesimport com.contentstack.sdk.Config;
import com.contentstack.sdk.Contentstack;
import com.contentstack.sdk.RetryOptions;
import com.contentstack.sdk.Stack;

public class AllowFiveRetries {
    public static void main(String[] args) throws IllegalAccessException {
        RetryOptions retryOptions = new RetryOptions().setRetryLimit(5);

        // Attach the options before creating the stack
        Config config = new Config();
        config.setRetryOptions(retryOptions);
        Stack stack = Contentstack.stack("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>", config);

        System.out.println(config.getRetryOptions().getRetryLimit()); // 5
    }
}Error handling: reject a limit above 10import com.contentstack.sdk.RetryOptions;

public class RejectRetryLimit {
    public static void main(String[] args) {
        RetryOptions retryOptions = new RetryOptions();
        try {
            retryOptions.setRetryLimit(11);
        } catch (IllegalArgumentException e) {
            // Thrown at the call for a limit below 0 or above 10
            System.out.println("Invalid retry limit: " + e.getMessage());
        }
        System.out.println(retryOptions.getRetryLimit()); // 3, the default, unchanged
    }
}
```

Maximum retries after the first attempt, from 0 to 10.

## setRetryableStatusCodes

setRetryableStatusCodes replaces the list of HTTP status codes that make the SDK retry a request.

```
Returns: RetryOptions. The same retry options, for chaining.
ValidationThrows IllegalArgumentException if any code is below 100 or above 599, because a value outside that range is not an HTTP status.The exception comes from setRetryableStatusCodes itself, at the call. The options keep their previous list, even when the other codes in the call are valid.No arguments, or a null array, is valid. It empties the list.BehaviorThe default list is 408, 429, 502, 503, and 504.Each call replaces the whole list. It does not add to the current list, so include every code you want retried.The SDK copies the codes. Changing your array after the call does not change the options.An empty list stops retries on status codes. The SDK still retries network errors, such as a timeout, up to the retry limit. To stop every retry, use the setRetryEnabled method.A response with a status outside the list goes to the callback after one attempt.The SDK accepts any code from 100 to 599, including success codes. A 2xx code in the list makes the SDK retry successful responses.ExampleBasic usage: retry only on 429 and 503import com.contentstack.sdk.Config;
import com.contentstack.sdk.Contentstack;
import com.contentstack.sdk.RetryOptions;
import com.contentstack.sdk.Stack;

import java.util.Arrays;

public class RetryOnRateLimit {
    public static void main(String[] args) throws IllegalAccessException {
        RetryOptions retryOptions = new RetryOptions().setRetryableStatusCodes(429, 503);

        // Attach the options before creating the stack
        Config config = new Config();
        config.setRetryOptions(retryOptions);
        Stack stack = Contentstack.stack("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>", config);

        System.out.println(Arrays.toString(config.getRetryOptions().getRetryableStatusCodes())); // [429, 503]
    }
}Edge case: retry network errors onlyimport com.contentstack.sdk.RetryOptions;

import java.util.Arrays;

public class RetryNetworkErrorsOnly {
    public static void main(String[] args) {
        // No codes: status retries stop, network error retries continue
        RetryOptions retryOptions = new RetryOptions().setRetryableStatusCodes();

        System.out.println(Arrays.toString(retryOptions.getRetryableStatusCodes())); // []
        System.out.println(retryOptions.isRetryEnabled());                            // true
    }
}Error handling: reject a code outside the HTTP rangeimport com.contentstack.sdk.RetryOptions;

import java.util.Arrays;

public class RejectStatusCode {
    public static void main(String[] args) {
        RetryOptions retryOptions = new RetryOptions();
        try {
            retryOptions.setRetryableStatusCodes(429, 600);
        } catch (IllegalArgumentException e) {
            // Thrown at the call for any code below 100 or above 599
            System.out.println("Invalid status code: " + e.getMessage());
        }
        System.out.println(Arrays.toString(retryOptions.getRetryableStatusCodes())); // the default list, unchanged
    }
}
```

HTTP statuses that trigger a retry, each from 100 to 599.

## toString

toString returns a one-line description of the retry settings, for logs.

```
Returns: String. A one-line summary of the settings.
ValidationThere is no client-side validation.toString does not throw.BehaviorThe text has the shape RetryOptions{enabled=true, limit=3, delay=1000ms, strategy=EXPONENTIAL, retryableCodes=[408, 429, 502, 503, 504]}, shown here with the defaults.The text does not include the custom backoff strategy. A strategy=CUSTOM value means the options hold one.The SDK writes the same text to its java.util.logging log at level FINE when it adds the retry step to a new stack.Use the text for logs only. The format can change in any release, so read individual values with the getters.ExampleBasic usage: log the retry settingsimport com.contentstack.sdk.RetryOptions;

public class LogRetryOptions {
    public static void main(String[] args) {
        RetryOptions retryOptions = new RetryOptions().setRetryLimit(5).setRetryDelay(500);
        System.out.println(retryOptions);
        // RetryOptions{enabled=true, limit=5, delay=500ms, strategy=EXPONENTIAL, retryableCodes=[408, 429, 502, 503, 504]}
    }
}
```

setRetryEnabled, isRetryEnabled. Turns the retry step on or off.

setRetryLimit, getRetryLimit. Maximum number of retries, from 0 to 10.

setRetryDelay, getRetryDelay. Base wait between attempts, in milliseconds.

setBackoffStrategy, getBackoffStrategy. How the wait grows from one retry to the next.

setRetryableStatusCodes, getRetryableStatusCodes. HTTP statuses that trigger a retry.

setCustomBackoffStrategy, getCustomBackoffStrategy, hasCustomBackoff. Your own wait calculation.

## RetryOptions | Java Delivery SDK | Contentstack

The RetryOptions class in the Java Delivery SDK sets how many times, how long apart, and on which status codes the SDK retries a failed request.