Skip to content

OpenAI Java lib 4.41.0 throws OpenAIInvalidDataException in OrganizationCostsResult when parsing amount values #769

Description

@jlangch

Environment:

  • Java VM: Java JDK 8, Java JDK 21 (Azul Systems, Inc.)
  • OpenAI Java: 4.41.0, 4.42.0

Description:

Parsing the amount from a OrganizationCostsResult to a value and currency throws an OpenAIInvalidDataException when amount values like 0E-6176 or 0.003627500000000000000000000000000000 are received.

Code snippet:

OrganizationCostsResult costs = result.asOrganizationCosts();

double value = costs.amount()
                    .flatMap(amount -> amount.value())  // <== exception on amount.value()
                    .orElse(0.0);

Exception thrown:

Exception in thread "main" com.openai.errors.OpenAIInvalidDataException: `value` is invalid, received 0E-6176
    at com.openai.core.JsonField.getOptional$openai_java_core(Values.kt:191)
    at com.openai.models.admin.organization.usage.UsageCostsResponse$Data$Result$OrganizationCostsResult$Amount.value(UsageCostsResponse.kt:6680)
    at com.github.jlangch.venice.util.openai.bugs.UsageCostJsonParserBug.lambda$2(UsageCostJsonParserBug.java:72)
    at java.util.Optional.flatMap(Optional.java:241)
    at com.github.jlangch.venice.util.openai.bugs.UsageCostJsonParserBug.lambda$1(UsageCostJsonParserBug.java:72)
    at java.util.ArrayList.forEach(ArrayList.java:1259)
    at com.github.jlangch.venice.util.openai.bugs.UsageCostJsonParserBug.lambda$0(UsageCostJsonParserBug.java:59)
    at java.util.ArrayList.forEach(ArrayList.java:1259)
    at com.github.jlangch.venice.util.openai.bugs.UsageCostJsonParserBug.main(UsageCostJsonParserBug.java:55)

Some additional OpenAIInvalidDataException messages with other values that failed parsing:

`value` is invalid, received 0E-6176
`value` is invalid, received 0.003627500000000000000000000000000000
`value` is invalid, received 0.007605000000000000000000000000000000

Example code demonstrating the parser exception:

package com.github.jlangch.venice.util.openai.bugs;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;

import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.admin.organization.usage.UsageCostsParams;
import com.openai.models.admin.organization.usage.UsageCostsResponse;
import com.openai.models.admin.organization.usage.UsageCostsResponse.Data.Result.OrganizationCostsResult;


/**
 * Demonstrates the openai-java <code>OrganizationCostsResult</code> value parser bug
 *
 * Depends on OpenAI:  com.openai:openai-java:4.41.0
 */
public class UsageCostJsonParserBug {

    private UsageCostJsonParserBug() {}

    public static void main(String[] args) {
        final OpenAIClient client = OpenAIOkHttpClient
                                        .builder()
                                        .adminApiKey(System.getenv("OPENAI_ADMIN_KEY"))
                                        .build();

        final long startTime = LocalDate
                                .now(ZoneOffset.UTC)
                                .minusDays(10)
                                .atStartOfDay(ZoneOffset.UTC)
                                .toEpochSecond();

        final long endTime = Instant.now().getEpochSecond();

        final UsageCostsParams params = UsageCostsParams
                                            .builder()
                                            .startTime(startTime)
                                            .endTime(endTime)
                                            .bucketWidth(UsageCostsParams.BucketWidth._1D)
                                            .addGroupBy(UsageCostsParams.GroupBy.PROJECT_ID)
                                            .addGroupBy(UsageCostsParams.GroupBy.API_KEY_ID)
                                            .addGroupBy(UsageCostsParams.GroupBy.LINE_ITEM)
                                            .limit(180)
                                            .build();

        final UsageCostsResponse response = client
                                                .admin()
                                                .organization()
                                                .usage()
                                                .costs(params);

        response.data().forEach(bucket -> {
            final LocalDateTime bucketStart = toLocalDateTime(bucket.startTime());
            final LocalDateTime bucketEnd = toLocalDateTime(bucket.endTime());

            bucket.results().forEach(result -> {
                if (!result.isOrganizationCosts()) {
                    return;
                }

                final OrganizationCostsResult costs = result.asOrganizationCosts();

                try {
                    final double value;

                    if (true) {
                        // The OpenAI value Json number parser throws an exception
                        value = costs.amount()
                                     .flatMap(amount -> amount.value())  // <== exception
                                     .orElse(0.0);
                    }
                    else {
                        // This custom JsonField number parser works fine
                        value = Double.parseDouble(
                                    costs.amount().isPresent()
                                        ? costs.amount().get()._value().asString().orElse("0.0")
                                        : "0.0");
                    }

                    final String currency = costs.amount()
                                                 .flatMap(amount -> amount.currency())
                                                 .orElse("unknown");

                    final String lineItem = costs.lineItem().orElse("-");

                    System.out.println(String.format(
                            "%s – %s | %.6f %s | %s",
                            bucketStart,
                            bucketEnd,
                            value,
                            currency,
                            lineItem));
                }
                catch(Exception ex) {
                    /*
                       Exception:

                       com.openai.errors.OpenAIInvalidDataException: `value` is invalid, received 0E-6176
                          at com.openai.core.JsonField.getOptional$openai_java_core(Values.kt:191)
                          at com.openai.models.admin.organization.usage.UsageCostsResponse$Data$Result$OrganizationCostsResult$Amount.value(UsageCostsResponse.kt:6680)

                       Some exception messages:

                          `value` is invalid, received 0E-6176
                          `value` is invalid, received 0.003627500000000000000000000000000000
                          `value` is invalid, received 0.007605000000000000000000000000000000


                        Exception in thread "main" com.openai.errors.OpenAIInvalidDataException: `value` is invalid, received 0E-6176
                            at com.openai.core.JsonField.getOptional$openai_java_core(Values.kt:191)
                            at com.openai.models.admin.organization.usage.UsageCostsResponse$Data$Result$OrganizationCostsResult$Amount.value(UsageCostsResponse.kt:6680)
                            at com.github.jlangch.venice.util.openai.bugs.UsageCostJsonParserBug.lambda$2(UsageCostJsonParserBug.java:72)
                            at java.util.Optional.flatMap(Optional.java:241)
                            at com.github.jlangch.venice.util.openai.bugs.UsageCostJsonParserBug.lambda$1(UsageCostJsonParserBug.java:72)
                            at java.util.ArrayList.forEach(ArrayList.java:1259)
                            at com.github.jlangch.venice.util.openai.bugs.UsageCostJsonParserBug.lambda$0(UsageCostJsonParserBug.java:59)
                            at java.util.ArrayList.forEach(ArrayList.java:1259)
                            at com.github.jlangch.venice.util.openai.bugs.UsageCostJsonParserBug.main(UsageCostJsonParserBug.java:55)
                     */
                    System.out.println("Exception: " + ex.getMessage());

                    throw ex;
                }
            });
        });
    }


    private static LocalDateTime toLocalDateTime(final long epochSeconds) {
        return LocalDateTime.ofInstant(
                Instant.ofEpochSecond(epochSeconds),
                ZoneOffset.UTC);
    }

}

If the custom JsonField number parser in the above example is activated the cost items are fine. NoOpenAIInvalidDataExceptions:

2026-06-26T00:00 – 2026-06-27T00:00 | 0.000000 usd | gpt-5.4-2026-03-05, cached input
2026-06-26T00:00 – 2026-06-27T00:00 | 0.003210 usd | gpt-5.4-2026-03-05, input
2026-06-26T00:00 – 2026-06-27T00:00 | 0.008490 usd | gpt-5.4-2026-03-05, output
2026-06-26T00:00 – 2026-06-27T00:00 | 0.000000 usd | gpt-5-search-api-2025-10-14, cached input
2026-06-26T00:00 – 2026-06-27T00:00 | 0.059730 usd | gpt-5-search-api-2025-10-14, input
2026-06-26T00:00 – 2026-06-27T00:00 | 0.010290 usd | gpt-5-search-api-2025-10-14, output
2026-06-26T00:00 – 2026-06-27T00:00 | 0.020000 usd | web search tool calls
2026-06-27T00:00 – 2026-06-28T00:00 | 0.000000 usd | gpt-5.4-2026-03-05, cached input
2026-06-27T00:00 – 2026-06-28T00:00 | 0.002495 usd | gpt-5.4-2026-03-05, input
2026-06-27T00:00 – 2026-06-28T00:00 | 0.003525 usd | gpt-5.4-2026-03-05, output
2026-06-28T00:00 – 2026-06-29T00:00 | 0.000000 usd | gpt-5.4-2026-03-05, cached input
2026-06-28T00:00 – 2026-06-29T00:00 | 0.027858 usd | gpt-5.4-2026-03-05, input
2026-06-28T00:00 – 2026-06-29T00:00 | 0.106335 usd | gpt-5.4-2026-03-05, output
2026-06-28T00:00 – 2026-06-29T00:00 | 0.000000 usd | gpt-5.5-2026-04-23, cached input
2026-06-28T00:00 – 2026-06-29T00:00 | 0.083110 usd | gpt-5.5-2026-04-23, input
2026-06-28T00:00 – 2026-06-29T00:00 | 0.625530 usd | gpt-5.5-2026-04-23, output

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions