spring-projects / spring-projects/spring-ai

OpenAI auto-config startup fails when only a sub-prefix credential is set

Open
#5,989 0 comments 0 reactions 1 assignee View on GitHub

@sdeleuze is already working on this.

Since May 22, 2026.

bug configuration openai
Dominant language
Java
Stars
9.5k
Forks
2.9k
Avg merge
1d 7h
Merged PRs (30d)
6

Description

Background

While digging into #1818 (spring.ai.openai.image.api-key alone fails the context), I traced the failure to how OpenAI auto-configuration gates its model beans, not to anything image-specific. The same shape of bug applies to chat, embedding, audio.speech, audio.transcription, and moderation — image is just the first one that surfaced because that's the path the
original reporter took.

I'm filing this as a separate issue (with a follow-up PR) because the fix touches all six sub-configurations and introduces a small new Condition, which feels closer to a refactor than a single-bean bug fix. The change closes #1818 as a consequence. Happy to fold it back into #1818 if you'd prefer that flow.

Reproduction

# application.yml — startup fails with only this set
spring:
  ai:
    openai:
      image:
        api-key: sk-xxx
java.lang.IllegalStateException: At least one credential source must be specified:
    credential (apiKey), workloadIdentity, or adminApiKey
    at com.openai.core.ClientOptions$Builder.effectiveCredential(ClientOptions.kt:503)
    ... (wrapped by Spring as BeanCreationException during context startup)

Substitute chat, embedding, audio.speech, audio.transcription, or moderation for image above and the failure is identical — only the failing sub-configuration changes. Programmatic credentials, the OPENAI_API_KEY env var, and Microsoft Foundry passwordless setups exhibit the same shape: one valid credential source, five other
sub-configurations still try to register and fail fast.

Current behavior

The six OpenAI auto-configurations gate registration only on spring.ai.openai.api-key (the common prefix), even though Spring AI already accepts several other valid credential sources at the SDK level:

  • spring.ai.openai.<chat|image|embedding|audio.speech|audio.transcription|moderation>.api-key
  • a programmatic Credential set on OpenAiCommonProperties or any of the sub-*Properties
  • OPENAI_API_KEY env var (default OpenAI endpoint)
  • Microsoft Foundry endpoints, where the api-key may legitimately be absent because passwordless auth is used. Detected by any of:
    • spring.ai.openai.microsoft-foundry=true
    • base-url ending in openai.azure.com or cognitiveservices.azure.com
    • microsoft-deployment-name, deployment-name, or microsoft-foundry-service-version set

In all of these cases, all six @Configuration classes still register; OpenAiSetup then fails fast on the sub-configurations that have no usable credential, and the failure takes the whole application context down before the working sub-configuration ever boots.

Why a custom Condition

The first thing I tried was widening the existing @ConditionalOnProperty — the same shape that's been attempted before:

@ConditionalOnProperty(
    prefix = "spring.ai.openai",
    name = { "api-key", "image.api-key" },
    matchIfMissing = false
)

That doesn't work: Spring Boot's name = {a, b} semantics is AND, not OR — it matches only when both keys are set, which is the opposite of what we need. The condition needs OR-of-credential-sources logic, which I don't see a way to express declaratively without a custom SpringBootCondition.

Proposed change

A single OpenAiConnectionCondition utility with one inner condition per sub-configuration (Chat, Image, Embedding, AudioSpeech, AudioTranscription, Moderation). Each one independently checks the credential sources listed above with short-circuit OR semantics. If none match, the corresponding @Bean does not register, and the rest of the context boots normally.

The conditions sit on the @Bean method, not the @Configuration class, so:

  • @EnableConfigurationProperties(OpenAi*Properties) still runs — the properties bean stays available for other code that injects it.
  • The existing model-selector @ConditionalOnProperty(spring.ai.model.<kind>) keeps working unchanged.

A working branch with this change and 13 tests covering each credential source per sub-configuration is ready (all green on the spring-ai-autoconfigure-model-openai module). I'll open the PR right after this issue if the direction looks reasonable.

Scope / non-goals

  • No public API changes, no new dependencies.
  • No behavior change for users who already set spring.ai.openai.api-key — that branch matches first, exactly as today.
  • Closes #1818 as a side effect.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.