---
title: Java Feature Flags
description: Set up Datadog Feature Flags for Java applications.
breadcrumbs: Docs > Feature Flags > Server-Side Feature Flags > Java Feature Flags
---

> For the complete documentation index, see [llms.txt](https://docs.datadoghq.com/llms.txt).

# Java Feature Flags

{% callout %}
# Important note for users on the following Datadog sites: app.ddog-gov.com, us2.ddog-gov.com

{% alert level="danger" %}
This product is not supported for your selected [Datadog site](https://docs.datadoghq.com/getting_started/site.md). ({% placeholder "user-datadog-site-name" /%}).
{% /alert %}

{% /callout %}

## Overview{% #overview %}

This page describes how to add Datadog Feature Flags to a Java application. Starting in version 1.65.0, `dd-openfeature` loads flag configuration directly from the Datadog-managed CDN by default. This agentless source simplifies onboarding for long-running servers and supports serverless runtimes that cannot connect to a Datadog Agent.

The Datadog provider implements the [OpenFeature](https://openfeature.dev/) standard. It uses `dd-java-agent` for configuration delivery. Agentless delivery removes the external Datadog Agent requirement, but `dd-java-agent` must still load in the JVM.

{% alert level="warning" %}
Starting in version 1.65.0, agentless mode changes only flag configuration. Java still requires a supported Datadog Agent or serverless telemetry path to export evaluation metrics or exposure events. Without such a path, only configuration delivery and local flag evaluation work.
{% /alert %}

## Compatibility requirements{% #compatibility-requirements %}

For the default agentless setup, you need:

- **Java 11 or higher**
- **Datadog Java agent** (`dd-java-agent`, loaded with `-javaagent`): Version **1.65.0** or later
- **Datadog OpenFeature provider** (`com.datadoghq:dd-openfeature`, added as a build dependency): Version **1.65.0** or later
- **OpenFeature SDK**: Version **1.20.1** or later
- A Datadog [**API key**](https://docs.datadoghq.com/account_management/api-app-keys.md#api-keys)
- Your Datadog [**site**](https://docs.datadoghq.com/getting_started/site.md)

Use the same version of `dd-java-agent` and `dd-openfeature`. Agentless delivery does not require a separate Datadog Agent service.

{% alert level="info" %}
For serverless Java, the runtime must support the `-javaagent` JVM option. You can pass the option in the Java command or through `JAVA_TOOL_OPTIONS`. See the Java setup for [Cloud Run Functions](https://docs.datadoghq.com/serverless/google_cloud_run/functions/java.md?tab=maven) or [Cloud Run containers](https://docs.datadoghq.com/serverless/google_cloud_run/containers/in_container/java.md) for examples.
{% /alert %}

For a full list of Datadog's Java version and framework support, read [Compatibility Requirements](https://docs.datadoghq.com/tracing/trace_collection/compatibility/java.md).

## Getting started{% #getting-started %}

Install the OpenFeature dependencies and add the Java agent to the JVM.

## Installation{% #installation %}

You need the Datadog OpenFeature provider and the OpenFeature SDK dependencies.

{% tab title="Gradle (Groovy)" %}
Add the following dependencies to your `build.gradle`:

In the `build.gradle` file:

```groovy
dependencies {
    // OpenFeature SDK for flag evaluation
    implementation 'dev.openfeature:sdk:1.20.1'

    // Datadog OpenFeature Provider
    implementation 'com.datadoghq:dd-openfeature:1.65.0'
}
```

{% /tab %}

{% tab title="Gradle (Kotlin)" %}
Add the following dependencies to your `build.gradle.kts`:

In the `build.gradle.kts` file:

```kotlin
dependencies {
    // OpenFeature SDK for flag evaluation
    implementation("dev.openfeature:sdk:1.20.1")

    // Datadog OpenFeature Provider
    implementation("com.datadoghq:dd-openfeature:1.65.0")
}
```

{% /tab %}

{% tab title="Maven" %}
Add the following dependencies to your `pom.xml`:

In the `pom.xml` file:

```xml
<dependencies>
    <!-- OpenFeature SDK for flag evaluation -->
    <dependency>
        <groupId>dev.openfeature</groupId>
        <artifactId>sdk</artifactId>
        <version>1.20.1</version>
    </dependency>

    <!-- Datadog OpenFeature Provider -->
    <dependency>
        <groupId>com.datadoghq</groupId>
        <artifactId>dd-openfeature</artifactId>
        <version>1.65.0</version>
    </dependency>
</dependencies>
```

{% /tab %}

The Gradle and Maven installation examples pin specific versions of `dd-openfeature` and the OpenFeature SDK. See Compatibility requirements for the minimum supported versions.

Flag evaluation metrics use a separate telemetry path from flag configuration. See [Set Up Server-Side Flag Evaluation Metrics](https://docs.datadoghq.com/feature_flags/guide/server_flag_evaluation_metrics.md).

### Add the Java agent to the JVM{% #add-the-java-agent-to-the-jvm %}

Load `dd-java-agent` with the `-javaagent` JVM option. For installation instructions, see [Add the Java SDK to the JVM](https://docs.datadoghq.com/tracing/trace_collection/automatic_instrumentation/dd_libraries/java.md#add-the-java-sdk-to-the-jvm).

If the runtime controls the Java command, set the option through `JAVA_TOOL_OPTIONS`. See the Java setup for [Cloud Run Functions](https://docs.datadoghq.com/serverless/google_cloud_run/functions/java.md?tab=maven) or [Cloud Run containers](https://docs.datadoghq.com/serverless/google_cloud_run/containers/in_container/java.md) for examples.

## Configuration{% #configuration %}

### Configure agentless delivery{% #configure-agentless-delivery %}

Configure the API key, Datadog site, and environment in the application process:

```bash
export DD_API_KEY=<YOUR_API_KEY>
export DD_SITE=<YOUR_DATADOG_SITE>
export DD_ENV=<YOUR_ENVIRONMENT>
export DD_SERVICE=<YOUR_SERVICE_NAME>
export DD_VERSION=<YOUR_APP_VERSION>

java -javaagent:/path/to/dd-java-agent.jar -jar your-application.jar
```

No Feature Flags enablement or source setting is required. Initialize the Datadog OpenFeature provider to begin polling.

## Initialize the OpenFeature provider{% #initialize-the-openfeature-provider %}

Initialize the Datadog OpenFeature provider in your application startup code. The provider starts the selected configuration source.

```java
import dev.openfeature.sdk.OpenFeatureAPI;
import dev.openfeature.sdk.Client;
import datadog.trace.api.openfeature.Provider;
import dev.openfeature.sdk.exceptions.ProviderNotReadyError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class App {
    private static final Logger logger = LoggerFactory.getLogger(App.class);
    private static Client client;

    public static void main(String[] args) throws Exception {
        // Initialize the Datadog provider
        logger.info("Initializing Datadog OpenFeature Provider...");
        OpenFeatureAPI api = OpenFeatureAPI.getInstance();

        try {
            // Set provider and wait for initial configuration (recommended)
            api.setProviderAndWait(new Provider());
            client = api.getClient("my-app");
            logger.info("OpenFeature provider initialized successfully");
        } catch (ProviderNotReadyError e) {
            // Handle gracefully - app will use default flag values
            logger.warn("Provider not ready (configuration unavailable), continuing with defaults", e);
            client = api.getClient("my-app");
            logger.info("App will use default flag values until provider is ready");
        } catch (Exception e) {
            logger.error("Failed to initialize OpenFeature provider", e);
            throw e;
        }

        // Your application code here
    }
}
```

Use `setProviderAndWait()` to block evaluation until the selected source provides the initial flag configuration. This loads flags before the application starts serving traffic. The default initialization timeout is 30 seconds.

`ProviderNotReadyError` is an OpenFeature SDK exception thrown when the provider times out during initialization. Catching it allows the application to start with default flag values if configuration delivery is unavailable. If not caught, the exception propagates and may prevent application startup. Handle this based on your availability requirements.

### Asynchronous initialization{% #asynchronous-initialization %}

For non-blocking initialization, use `setProvider()` and listen for provider events:

```java
import dev.openfeature.sdk.ProviderEvent;

OpenFeatureAPI api = OpenFeatureAPI.getInstance();
Client client = api.getClient();

// Listen for provider state changes
client.on(ProviderEvent.PROVIDER_READY, (event) -> {
    logger.info("Feature flags ready!");
});

client.on(ProviderEvent.PROVIDER_ERROR, (event) -> {
    logger.error("Provider error: {}", event.getMessage());
});

client.on(ProviderEvent.PROVIDER_STALE, (event) -> {
    logger.warn("Provider configuration is stale");
});

// Set provider asynchronously
api.setProvider(new Provider());
```

## Set the evaluation context{% #set-the-evaluation-context %}

The evaluation context defines the subject (user, device, session) for flag evaluation. It determines which flag variations are returned based on targeting rules.

{% alert level="warning" %}
Datadog Feature Flags requires evaluation context attributes to be flat primitive values: strings, numbers, and Booleans. Do not pass nested objects or arrays; they are not supported and can cause exposure data to be dropped.
{% /alert %}

```java
import dev.openfeature.sdk.EvaluationContext;
import dev.openfeature.sdk.MutableContext;

// Create an evaluation context with a targeting key and attributes
EvaluationContext context = new MutableContext("user-123")
    .add("email", "user@example.com")
    .add("tier", "premium");

// Use the context for flag evaluations (see next section)
```

The `targetingKey` (for example, `user-123`) is the primary identifier used for consistent flag evaluations and percentage-based rollouts. It's typically a user ID, session ID, or device ID.

## Evaluate flags{% #evaluate-flags %}

Evaluate feature flags using the OpenFeature client. All flag types are supported: Boolean, string, integer, double, and object.

{% tab title="Boolean" %}

```java
// Simple Boolean evaluation
boolean enabled = client.getBooleanValue("checkout.new", false, context);

if (enabled) {
    // New checkout flow
} else {
    // Old checkout flow
}

// Get detailed evaluation result
import dev.openfeature.sdk.FlagEvaluationDetails;

FlagEvaluationDetails<Boolean> details =
    client.getBooleanDetails("checkout.new", false, context);

logger.info("Value: {}", details.getValue());
logger.info("Variant: {}", details.getVariant());
logger.info("Reason: {}", details.getReason());
```

{% /tab %}

{% tab title="String" %}

```java
// Evaluate string flags (e.g., UI themes, API endpoints)
String theme = client.getStringValue("ui.theme", "light", context);

String apiEndpoint = client.getStringValue(
    "payment.api.endpoint",
    "https://api.example.com/v1",
    context
);
```

{% /tab %}

{% tab title="Number" %}

```java
// Integer flags (e.g., limits, quotas)
int maxRetries = client.getIntegerValue("retries.max", 3, context);

// Double flags (e.g., thresholds, rates)
double discountRate = client.getDoubleValue("pricing.discount.rate", 0.0, context);
```

{% /tab %}

{% tab title="Object" %}

```java
import dev.openfeature.sdk.Value;

// Evaluate object/JSON flags for complex configuration
Value config = client.getObjectValue("ui.config", new Value(), context);

// Access structured data
if (config.isStructure()) {
    Value timeout = config.asStructure().getValue("timeout");
    Value endpoint = config.asStructure().getValue("endpoint");
}
```

{% /tab %}

## Error handling{% #error-handling %}

The OpenFeature SDK uses a default value pattern. If evaluation fails for any reason, the default value you provide is returned.

```java
import dev.openfeature.sdk.ErrorCode;

// Check evaluation details for errors
FlagEvaluationDetails<Boolean> details =
    client.getBooleanDetails("checkout.new", false, context);

if (details.getErrorCode() != null) {
    switch (details.getErrorCode()) {
        case FLAG_NOT_FOUND:
            logger.warn("Flag does not exist: {}", "checkout.new");
            break;
        case PROVIDER_NOT_READY:
            logger.warn("Provider not initialized yet");
            break;
        case TARGETING_KEY_MISSING:
            logger.warn("Evaluation context missing targeting key");
            break;
        case TYPE_MISMATCH:
            logger.error("Flag value type doesn't match requested type");
            break;
        default:
            logger.error("Evaluation error for flag: {}", "checkout.new", details.getErrorCode());
    }
}
```

### Common error codes{% #common-error-codes %}

| Error Code              | Description                                     | Resolution                                                     |
| ----------------------- | ----------------------------------------------- | -------------------------------------------------------------- |
| `PROVIDER_NOT_READY`    | Initial configuration not received              | Wait for provider initialization or use `setProviderAndWait()` |
| `FLAG_NOT_FOUND`        | Flag doesn't exist in configuration             | Check flag key or create flag in Datadog UI                    |
| `TARGETING_KEY_MISSING` | No targeting key in evaluation context          | Provide a targeting key when creating context                  |
| `TYPE_MISMATCH`         | Flag value can't be converted to requested type | Use correct evaluation method for flag type                    |
| `INVALID_CONTEXT`       | Evaluation context is null                      | Provide a valid evaluation context                             |

## Advanced configuration{% #advanced-configuration %}

Use [Server SDK Configuration Sources](https://docs.datadoghq.com/feature_flags/concepts/configuration_sources.md) as the canonical reference for source selection and operational settings:

- [Configure agentless delivery](https://docs.datadoghq.com/feature_flags/concepts/configuration_sources.md#configure-agentless-delivery), including polling, request timeout, and endpoint settings
- [Use a custom agentless endpoint](https://docs.datadoghq.com/feature_flags/concepts/configuration_sources.md#use-a-custom-agentless-endpoint) for advanced testing, local development, or an operator-managed proxy
- [Use Agent Remote Configuration](https://docs.datadoghq.com/feature_flags/concepts/configuration_sources.md#use-agent-remote-configuration) to retain Agent-managed delivery
- [Migrate an existing Remote Configuration setup](https://docs.datadoghq.com/feature_flags/concepts/configuration_sources.md#migrate-an-existing-remote-configuration-setup) and remove the deprecated `DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED` setting

Agentless mode changes only flag configuration. It does not configure or enable `feature_flag.evaluations`, exposure logging, or experimentation use cases. These features require a supported Datadog Agent or serverless telemetry path. For more information on available graphing, see [Feature Flag Graphs](https://docs.datadoghq.com/feature_flags/concepts/flag_graphs.md).

### Custom initialization timeout{% #custom-initialization-timeout %}

Configure how long the provider waits for initial configuration:

```java
import datadog.trace.api.openfeature.Provider;
import java.util.concurrent.TimeUnit;

Provider.Options options = new Provider.Options()
    .initTimeout(10, TimeUnit.SECONDS);

api.setProviderAndWait(new Provider(options));
```

### Configuration change events{% #configuration-change-events %}

Listen for updates from the selected configuration source:

```java
import dev.openfeature.sdk.ProviderEvent;

client.on(ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, (event) -> {
    logger.info("Flag configuration updated: {}", event.getMessage());
    // Optionally re-evaluate flags or trigger cache refresh
});
```

`PROVIDER_CONFIGURATION_CHANGED` is an optional OpenFeature event. Check the Datadog provider documentation to verify this event is supported in your version.

### Multiple clients{% #multiple-clients %}

Use named clients to organize context and flags by domain or team:

```java
// Named clients share the same provider instance but can have different contexts
Client checkoutClient = api.getClient("checkout");
Client analyticsClient = api.getClient("analytics");

// Each client can have its own evaluation context
EvaluationContext checkoutContext = new MutableContext("session-abc");
EvaluationContext analyticsContext = new MutableContext("user-123");

boolean newCheckout = checkoutClient.getBooleanValue(
    "checkout.ui.new", false, checkoutContext
);

boolean enhancedAnalytics = analyticsClient.getBooleanValue(
    "analytics.enhanced", false, analyticsContext
);
```

The `Provider` instance is shared globally. Client names are for organizational purposes only and don't create separate provider instances. All clients use the same underlying Datadog provider and flag configurations.

## Best practices{% #best-practices %}

### Initialize early{% #initialize-early %}

Initialize the OpenFeature provider as early as possible in your application lifecycle (for example, in `main()` or application startup). This helps ensure flags are ready before business logic executes.

### Use meaningful default values{% #use-meaningful-default-values %}

Always provide sensible default values that maintain safe behavior if flag evaluation fails:

```java
// Good: Safe default that maintains current behavior
boolean useNewAlgorithm = client.getBooleanValue("algorithm.new", false, context);

// Good: Conservative default for limits
int rateLimit = client.getIntegerValue("rate.limit", 100, context);
```

### Create context once{% #create-context-once %}

Create the evaluation context once per request/user/session and reuse it for all flag evaluations:

```java
// In a web filter or request handler
EvaluationContext userContext = new MutableContext(userId)
    .add("email", user.getEmail())
    .add("tier", user.getTier());

// Reuse context for all flags in this request
boolean featureA = client.getBooleanValue("feature.a", false, userContext);
boolean featureB = client.getBooleanValue("feature.b", false, userContext);
```

Rebuilding the evaluation context for every flag evaluation adds unnecessary overhead. Create the context once at the start of the request lifecycle, then pass it to all subsequent flag evaluations.

### Handle initialization failures (optional){% #handle-initialization-failures-optional %}

Consider handling initialization failures if your application can function with default flag values:

```java
try {
    api.setProviderAndWait(new Provider());
} catch (ProviderNotReadyError e) {
    // Log error and continue with defaults
    logger.warn("Feature flags not ready, using defaults", e);
    // Application will use default values for all flags
}
```

If feature flags are critical for your application to function, let the exception propagate to prevent startup.

### Use consistent targeting keys{% #use-consistent-targeting-keys %}

Use consistent, stable identifiers as targeting keys:

- **Good**: User IDs, session IDs, device IDs
- **Avoid**: Timestamps, random values, frequently changing IDs

### Monitor flag evaluation{% #monitor-flag-evaluation %}

Use the detailed evaluation results for logging and debugging:

```java
FlagEvaluationDetails<Boolean> details =
    client.getBooleanDetails("feature.critical", false, context);

logger.info("Flag: {} | Value: {} | Variant: {} | Reason: {}",
    "feature.critical",
    details.getValue(),
    details.getVariant(),
    details.getReason()
);
```

## Testing{% #testing %}

You can test against a dedicated Datadog test environment with the real `DatadogProvider`, or swap it for OpenFeature's `InMemoryProvider` to control flag values directly in test code. This section shows the in-memory approach, which keeps tests hermetic and offline. `InMemoryProvider` ships in `dev.openfeature:sdk` (already a test-scope dependency), so no additional library is required. Add `dev.openfeature:sdk` to your test configuration if it is not already present.

```java
import dev.openfeature.sdk.Client;
import dev.openfeature.sdk.OpenFeatureAPI;
import dev.openfeature.sdk.providers.memory.Flag;
import dev.openfeature.sdk.providers.memory.InMemoryProvider;
import java.util.Map;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;

class CheckoutFlagTest {
    private Client client;

    @BeforeEach
    void setUp() {
        Map<String, Flag<?>> flags = Map.of(
            "new-checkout-flow", Flag.<Boolean>builder()
                .variant("on", true)
                .variant("off", false)
                .defaultVariant("on")
                .build(),
            "ui-theme", Flag.<String>builder()
                .variant("dark", "dark")
                .variant("light", "light")
                .defaultVariant("light")
                .build()
        );

        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
        api.setProviderAndWait(new InMemoryProvider(flags));
        client = api.getClient();
    }

    @AfterEach
    void tearDown() {
        OpenFeatureAPI.getInstance().shutdown();
    }

    @Test
    void newCheckoutEnabledByDefault() {
        assertTrue(client.getBooleanValue("new-checkout-flow", false));
    }

    @Test
    void missingFlagReturnsDefault() {
        assertFalse(client.getBooleanValue("does-not-exist", false));
    }
}
```

`OpenFeatureAPI.getInstance()` is a singleton. Always call `shutdown()` in `@AfterEach` (or equivalent); otherwise, provider state leaks between test classes and causes flaky suites.

In Spring Boot tests, register the `InMemoryProvider` through a `@TestConfiguration` bean or in a `@BeforeAll` hook on an `@SpringBootTest` class — the OpenFeature API singleton persists for the lifetime of the Spring context, so initialization only needs to run once.

## Troubleshooting{% #troubleshooting %}

Follow the flag data path from the **Flagging Platform** through the selected configuration source to the **Java SDK**. Agentless and Remote Configuration have different requirements. Verify the active source before you troubleshoot connectivity.

### 1. Flagging platform: Verify flag configuration

Before checking infrastructure, confirm the flag itself is set up correctly:

1. The flag is **enabled** for the target environment, not disabled. Flags are disabled by default in each environment.
1. The flag targets the **correct environment** (`DD_ENV`). Flags do not target specific services—they apply to all services within the enabled environment.
1. Your `DD_ENV` value appears in [Feature Flag Environments](https://app.datadoghq.com/feature-flags/settings/environments). If it is absent, the environment has not received any flag traffic yet.

### 2. Verify the configuration source

#### Agentless{% #agentless %}

1. Confirm that `dd-openfeature` and `dd-java-agent` are version 1.65.0 or later. Use the same version for both components.
1. Confirm that the JVM loads `dd-java-agent` with `-javaagent`, either in the Java command or through `JAVA_TOOL_OPTIONS`.
1. Confirm that `DD_FEATURE_FLAGS_ENABLED` is unset or set to `true`.
1. Confirm that `DD_FEATURE_FLAGS_CONFIGURATION_SOURCE=agentless` is set, or that the source and legacy provider settings are not set.
1. Confirm that application code initializes the Datadog OpenFeature provider.
1. Confirm that `DD_API_KEY`, `DD_SITE`, and `DD_ENV` are configured in the application process.
1. Confirm that the application can make outbound HTTPS requests to Datadog.
1. Enable `DD_TRACE_DEBUG=true` and check for authentication, timeout, or malformed-payload messages from the Feature Flags agentless endpoint.

#### Agent Remote Configuration{% #agent-remote-configuration %}

1. Confirm that `dd-openfeature` and `dd-java-agent` are version 1.65.0 or later. Use the same version for both components.
1. Confirm that `DD_FEATURE_FLAGS_CONFIGURATION_SOURCE=remote_config` is set. During the migration window, `DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true` also selects Remote Configuration when no source is set.
1. Confirm that `DD_FEATURE_FLAGS_ENABLED` is unset or set to `true`.
1. Confirm that Agent 7.55 or later is running and reachable. See [APM Connection Errors](https://docs.datadoghq.com/tracing/troubleshooting/connection_errors.md).
1. Confirm that Remote Configuration is enabled on the Agent. If it is disabled, set `remote_configuration.enabled: true` in `datadog.yaml` or `DD_REMOTE_CONFIGURATION_ENABLED=true`. See [Remote Configuration](https://docs.datadoghq.com/remote_configuration.md).
1. Confirm that `DD_API_KEY` is valid on the Agent and belongs to the target organization.
1. Confirm that `DD_SITE` is set correctly on the Agent. See [Agent Site Issues](https://docs.datadoghq.com/agent/troubleshooting/site.md).
1. Run `datadog-agent status` and review the Remote Configuration section. See [Agent Commands](https://docs.datadoghq.com/agent/configuration/agent-commands.md).

### 3. SDK: Verify Java SDK state

#### Enable debug logging{% #enable-debug-logging %}

Set `DD_TRACE_DEBUG=true` to enable Feature Flags startup messages. For the default agentless source, confirm that CDN polling starts after provider initialization.

With `remote_config`, the provider uses the bridge in the Java agent. An older agent produces a provider initialization error that states the required agent version. It does not fall back to CDN delivery.

#### Monitor provider state changes{% #monitor-provider-state-changes %}

Add event listeners early in application startup to observe provider life cycle transitions. Event listeners detect connectivity changes after initialization:

```java
import dev.openfeature.sdk.ProviderEvent;

client.on(ProviderEvent.PROVIDER_READY, (event) -> {
    logger.info("Feature flag provider is ready");
});

client.on(ProviderEvent.PROVIDER_ERROR, (event) -> {
    logger.error("Feature flag provider error: {}", event.getMessage());
});

client.on(ProviderEvent.PROVIDER_STALE, (event) -> {
    logger.warn("Feature flag provider configuration is stale");
});

client.on(ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, (event) -> {
    logger.info("Feature flag configuration updated");
});
```

A `PROVIDER_ERROR` or `PROVIDER_STALE` event after normal operation indicates a disruption in the selected configuration source.

#### Provider not ready{% #provider-not-ready %}

`PROVIDER_NOT_READY` is returned when flag evaluation is attempted before the provider receives its first configuration from the selected source.

Common causes:

- **Asynchronous initialization**: `setProvider()` was used instead of `setProviderAndWait()`. Evaluations before the first configuration arrives return `PROVIDER_NOT_READY`.
- **Initialization timeout**: `setProviderAndWait()` timed out (default 30 seconds) and threw `ProviderNotReadyError`, which was caught. The application continues evaluating flags while waiting for the first configuration.

If `PROVIDER_NOT_READY` persists beyond the polling and initialization intervals, verify the selected source again.

#### Debug flag evaluations{% #debug-flag-evaluations %}

If flags return unexpected values, use `getBooleanDetails()` instead of `getBooleanValue()`. The `Details` variant returns a `FlagEvaluationDetails` object exposing the provider's internal state:

```java
FlagEvaluationDetails<Boolean> details =
    client.getBooleanDetails("your.flag.key", false, context);

logger.info("Flag evaluation details: value={}, variant={}, reason={}, errorCode={}",
    details.getValue(),
    details.getVariant(),
    details.getReason(),
    details.getErrorCode());
```

Review `reason` and `errorCode` to understand why the provider returned a given result.

#### Type mismatch errors{% #type-mismatch-errors %}

`TYPE_MISMATCH` is returned when the evaluation method does not match the flag's configured type. Use the correct method for each flag type: `getBooleanValue()`, `getStringValue()`, `getIntegerValue()`, `getDoubleValue()`.

### 4. Flagging platform: Verify data appears in Datadog

{% alert level="warning" %}
When no supported telemetry path is configured, Java does not export exposure events or the `feature_flag.evaluations` metric. Their absence does not indicate that configuration loading or local evaluation failed.
{% /alert %}

#### Flag evaluation metrics{% #flag-evaluation-metrics %}

Flag evaluation counts appear in Datadog as a `feature_flag.evaluations` counter metric tagged with the flag key, result variant, and evaluation reason. See [Set Up Server-Side Flag Evaluation Metrics](https://docs.datadoghq.com/feature_flags/guide/server_flag_evaluation_metrics.md) for the full setup guide and troubleshooting steps.

#### Experiment exposures{% #experiment-exposures %}

When the selected configuration path supports exposures, exposures appear only for flags associated with an experiment. Standard feature flags do not generate exposure events. If exposures are missing:

1. Verify the flag is associated with an experiment in the Datadog UI.
1. Verify the Agent API key and connectivity.

## Further reading{% #further-reading %}

Additional helpful documentation, links, and articles:

- [Server-Side Feature Flags](https://docs.datadoghq.com/feature_flags/server.md)
- [Java APM and Distributed Tracing](https://docs.datadoghq.com/tracing/trace_collection/automatic_instrumentation/dd_libraries/java.md)
- [Set Up Server-Side Flag Evaluation Metrics](https://docs.datadoghq.com/feature_flags/guide/server_flag_evaluation_metrics.md)
- [Feature Flag Graphs](https://docs.datadoghq.com/feature_flags/concepts/flag_graphs.md)
- [Server SDK Configuration Sources](https://docs.datadoghq.com/feature_flags/concepts/configuration_sources.md)
