This product is not supported for your selected Datadog site. ().
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 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.
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.
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
Use the same version of dd-java-agent and dd-openfeature. Agentless delivery does not require a separate Datadog Agent service.
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 or Cloud Run containers for examples.
Install the OpenFeature dependencies and add the Java agent to the JVM.
Installation
You need the Datadog OpenFeature provider and the OpenFeature SDK dependencies.
Add the following dependencies to your build.gradle:
build.gradle
dependencies{// OpenFeature SDK for flag evaluation
implementation'dev.openfeature:sdk:1.20.1'// Datadog OpenFeature Provider
implementation'com.datadoghq:dd-openfeature:1.65.0'}
Add the following dependencies to your build.gradle.kts:
build.gradle.kts
dependencies{// OpenFeature SDK for flag evaluation
implementation("dev.openfeature:sdk:1.20.1")// Datadog OpenFeature Provider
implementation("com.datadoghq:dd-openfeature:1.65.0")}
Add the following dependencies to your pom.xml:
pom.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>
The Gradle and Maven installation examples pin specific versions of dd-openfeature and the OpenFeature SDK. See Compatibility requirements for the minimum supported versions.
Load dd-java-agent with the -javaagent JVM option. For installation instructions, see 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 or Cloud Run containers for examples.
Configuration
Configure agentless delivery
Configure the API key, Datadog site, and environment in the application process:
No Feature Flags enablement or source setting is required. Initialize the Datadog OpenFeature provider to begin polling.
Initialize the OpenFeature provider
Initialize the Datadog OpenFeature provider in your application startup code. The provider starts the selected configuration source.
importdev.openfeature.sdk.OpenFeatureAPI;importdev.openfeature.sdk.Client;importdatadog.trace.api.openfeature.Provider;importdev.openfeature.sdk.exceptions.ProviderNotReadyError;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;publicclassApp{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(App.class);privatestaticClientclient;publicstaticvoidmain(String[]args)throwsException{// Initialize the Datadog providerlogger.info("Initializing Datadog OpenFeature Provider...");OpenFeatureAPIapi=OpenFeatureAPI.getInstance();try{// Set provider and wait for initial configuration (recommended)api.setProviderAndWait(newProvider());client=api.getClient("my-app");logger.info("OpenFeature provider initialized successfully");}catch(ProviderNotReadyErrore){// Handle gracefully - app will use default flag valueslogger.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(Exceptione){logger.error("Failed to initialize OpenFeature provider",e);throwe;}// 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
For non-blocking initialization, use setProvider() and listen for provider events:
importdev.openfeature.sdk.ProviderEvent;OpenFeatureAPIapi=OpenFeatureAPI.getInstance();Clientclient=api.getClient();// Listen for provider state changesclient.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 asynchronouslyapi.setProvider(newProvider());
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.
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.
importdev.openfeature.sdk.EvaluationContext;importdev.openfeature.sdk.MutableContext;// Create an evaluation context with a targeting key and attributesEvaluationContextcontext=newMutableContext("user-123").add("email","user@example.com").add("tier","premium");//Usethecontextforflagevaluations(seenextsection)
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 feature flags using the OpenFeature client. All flag types are supported: Boolean, string, integer, double, and object.
// Simple Boolean evaluationbooleanenabled=client.getBooleanValue("checkout.new",false,context);if(enabled){// New checkout flow}else{// Old checkout flow}// Get detailed evaluation resultimportdev.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());
// Evaluate string flags (e.g., UI themes, API endpoints)Stringtheme=client.getStringValue("ui.theme","light",context);StringapiEndpoint=client.getStringValue("payment.api.endpoint","https://api.example.com/v1",context);
importdev.openfeature.sdk.Value;// Evaluate object/JSON flags for complex configurationValueconfig=client.getObjectValue("ui.config",newValue(),context);// Access structured dataif(config.isStructure()){Valuetimeout=config.asStructure().getValue("timeout");Valueendpoint=config.asStructure().getValue("endpoint");}
Error handling
The OpenFeature SDK uses a default value pattern. If evaluation fails for any reason, the default value you provide is returned.
importdev.openfeature.sdk.ErrorCode;// Check evaluation details for errorsFlagEvaluationDetails<Boolean>details=client.getBooleanDetails("checkout.new",false,context);if(details.getErrorCode()!=null){switch(details.getErrorCode()){caseFLAG_NOT_FOUND:logger.warn("Flag does not exist: {}","checkout.new");break;casePROVIDER_NOT_READY:logger.warn("Provider not initialized yet");break;caseTARGETING_KEY_MISSING:logger.warn("Evaluation context missing targeting key");break;caseTYPE_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
Error Code
Description
Resolution
PROVIDER_NOT_READY
Initial configuration not received
Wait for provider initialization or use setProviderAndWait()
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.
Custom initialization timeout
Configure how long the provider waits for initial configuration:
PROVIDER_CONFIGURATION_CHANGED is an optional OpenFeature event. Check the Datadog provider documentation to verify this event is supported in your version.
Multiple clients
Use named clients to organize context and flags by domain or team:
// Named clients share the same provider instance but can have different contextsClientcheckoutClient=api.getClient("checkout");ClientanalyticsClient=api.getClient("analytics");// Each client can have its own evaluation contextEvaluationContextcheckoutContext=newMutableContext("session-abc");EvaluationContextanalyticsContext=newMutableContext("user-123");booleannewCheckout=checkoutClient.getBooleanValue("checkout.ui.new",false,checkoutContext);booleanenhancedAnalytics=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
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
Always provide sensible default values that maintain safe behavior if flag evaluation fails:
// Good: Safe default that maintains current behaviorbooleanuseNewAlgorithm=client.getBooleanValue("algorithm.new",false,context);// Good: Conservative default for limitsintrateLimit=client.getIntegerValue("rate.limit",100,context);
Create context once
Create the evaluation context once per request/user/session and reuse it for all flag evaluations:
// In a web filter or request handlerEvaluationContextuserContext=newMutableContext(userId).add("email",user.getEmail()).add("tier",user.getTier());// Reuse context for all flags in this requestbooleanfeatureA=client.getBooleanValue("feature.a",false,userContext);booleanfeatureB=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)
Consider handling initialization failures if your application can function with default flag values:
try{api.setProviderAndWait(newProvider());}catch(ProviderNotReadyErrore){// Log error and continue with defaultslogger.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, stable identifiers as targeting keys:
Good: User IDs, session IDs, device IDs
Avoid: Timestamps, random values, frequently changing IDs
Monitor flag evaluation
Use the detailed evaluation results for logging and debugging:
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.
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
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:
The flag is enabled for the target environment, not disabled. Flags are disabled by default in each environment.
The flag targets the correct environment (DD_ENV). Flags do not target specific services—they apply to all services within the enabled environment.
Your DD_ENV value appears in Feature Flag Environments. If it is absent, the environment has not received any flag traffic yet.
2. Verify the configuration source
Agentless
Confirm that dd-openfeature and dd-java-agent are version 1.65.0 or later. Use the same version for both components.
Confirm that the JVM loads dd-java-agent with -javaagent, either in the Java command or through JAVA_TOOL_OPTIONS.
Confirm that DD_FEATURE_FLAGS_ENABLED is unset or set to true.
Confirm that DD_FEATURE_FLAGS_CONFIGURATION_SOURCE=agentless is set, or that the source and legacy provider settings are not set.
Confirm that application code initializes the Datadog OpenFeature provider.
Confirm that DD_API_KEY, DD_SITE, and DD_ENV are configured in the application process.
Confirm that the application can make outbound HTTPS requests to Datadog.
Enable DD_TRACE_DEBUG=true and check for authentication, timeout, or malformed-payload messages from the Feature Flags agentless endpoint.
Agent Remote Configuration
Confirm that dd-openfeature and dd-java-agent are version 1.65.0 or later. Use the same version for both components.
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.
Confirm that DD_FEATURE_FLAGS_ENABLED is unset or set to true.
Confirm that Agent 7.55 or later is running and reachable. See APM Connection Errors.
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.
Confirm that DD_API_KEY is valid on the Agent and belongs to the target organization.
Confirm that DD_SITE is set correctly on the Agent. See Agent Site Issues.
Run datadog-agent status and review the Remote Configuration section. See Agent Commands.
3. SDK: Verify Java SDK state
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
Add event listeners early in application startup to observe provider life cycle transitions. Event listeners detect connectivity changes after initialization:
importdev.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 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
If flags return unexpected values, use getBooleanDetails() instead of getBooleanValue(). The Details variant returns a FlagEvaluationDetails object exposing the provider’s internal state:
Review reason and errorCode to understand why the provider returned a given result.
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
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.
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 for the full setup guide and troubleshooting steps.
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:
Verify the flag is associated with an experiment in the Datadog UI.
Verify the Agent API key and connectivity.
Further reading
Additional helpful documentation, links, and articles: