Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Azure OpenAI client fails with 401 when throttling #46109

Closed
dluc opened this issue Sep 21, 2024 · 6 comments · Fixed by #46401
Closed

[BUG] Azure OpenAI client fails with 401 when throttling #46109

dluc opened this issue Sep 21, 2024 · 6 comments · Fixed by #46401
Labels
Client This issue points to a problem in the data-plane of the library. customer-reported Issues that are reported by GitHub users external to the Azure organization. needs-team-attention Workflow: This issue needs attention from Azure service team or SDK team OpenAI question The issue doesn't require a change to the product in order to be resolved. Most issues start as that Service Attention Workflow: This issue is responsible by Azure service team.

Comments

@dluc
Copy link
Member

dluc commented Sep 21, 2024

Library name and version

Azure.AI.OpenAI 2.0.0-beta.5

Describe the bug

When using AzureOpenAIClient and sending too many requests, the Azure service throttling leads to a "401 Unauthorized" error instead of "429 Too Many Requests".
Looking at the internal requests, looks like the code is retrying on 429 as expected, sending a malformed request containing the Authorization header twice (with the same token).

Expected behavior

The client should keep retrying on 429 and/or fail with a HTTP exception status code 429

Actual behavior

The client fails with a HTTP exception status code 401

Reproduction Steps

using Azure.AI.OpenAI;
using Azure.Identity;
using OpenAI.Embeddings;

public static class Program
{
    public static async Task Main()
    {
        AzureOpenAIClient openAIClient = new(
            endpoint: new Uri("https://....openai.azure.com/"),
            credential: new DefaultAzureCredential());

        var embeddingClient = openAIClient.GetEmbeddingClient("text-embedding-ada-002");

        for (int i = 0; i < 200; i++)
        {
            Console.WriteLine($"## {i}");
            await embeddingClient.GenerateEmbeddingsAsync([RndStr(), RndStr(), RndStr(), RndStr(), RndStr(), RndStr(), RndStr()]);
        }
    }

    public static string RndStr()
    {
        var random = new Random();
        return new(Enumerable.Repeat(" ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 0123456789 ", 8000)
            .Select(s => s[random.Next(s.Length)]).ToArray());
    }
}

Output:

## 0
## 1
## 2
## 3
## 4
## 5
## 6
## 7
## 8
Unhandled exception. System.ClientModel.ClientResultException: Service request failed.
Status: 401 (Unauthorized)

   at Azure.AI.OpenAI.ClientPipelineExtensions.ProcessMessageAsync(ClientPipeline pipeline, PipelineMessage message, RequestOptions options)
   at Azure.AI.OpenAI.Embeddings.AzureEmbeddingClient.GenerateEmbeddingsAsync(BinaryContent content, RequestOptions options)
   at OpenAI.Embeddings.EmbeddingClient.GenerateEmbeddingsAsync(IEnumerable`1 inputs, EmbeddingGenerationOptions options, CancellationToken cancellationToken)
   at Program.Main() in Program.cs:line 38
   at Program.<Main>()

Environment

.NET SDK:
 Version:           8.0.401
 Commit:            811edcc344
 Workload version:  8.0.400-manifests.56cd0383
 MSBuild version:   17.11.4+37eb419ad

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  15.0
 OS Platform: Darwin
 RID:         osx-arm64
 Base Path:   /usr/local/share/dotnet/sdk/8.0.401
@github-actions github-actions bot added Client This issue points to a problem in the data-plane of the library. needs-team-attention Workflow: This issue needs attention from Azure service team or SDK team OpenAI Service Attention Workflow: This issue is responsible by Azure service team. labels Sep 21, 2024
Copy link

Thanks for the feedback! We are routing this to the appropriate team for follow-up. cc @jpalvarezl @ralph-msft @trrwilson.

@jsquire jsquire added customer-reported Issues that are reported by GitHub users external to the Azure organization. question The issue doesn't require a change to the product in order to be resolved. Most issues start as that labels Sep 22, 2024
@dluc
Copy link
Member Author

dluc commented Sep 22, 2024

FYI, I'm using this handler as a workaround:

public class AuthFixHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (request.Headers.TryGetValues("Authorization", out var headers) && headers.Count() > 1)
        {
            request.Headers.Authorization = new AuthenticationHeaderValue(
                request.Headers.Authorization.Scheme,
                request.Headers.Authorization.Parameter);
        }

        return base.SendAsync(request, cancellationToken);
    }
}

@alexmg
Copy link

alexmg commented Sep 26, 2024

A PipelinePolicy based workaround for anyone that may be interested.

public class AuthorizationHeaderWorkaroundPolicy : PipelinePolicy
{
    private const string AuthorizationHeaderName = "Authorization";

    public override void Process(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int currentIndex)
    {
        RemoveDuplicateHeaderValues(message.Request.Headers);

        ProcessNext(message, pipeline, currentIndex);
    }

    public override async ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int currentIndex)
    {
        RemoveDuplicateHeaderValues(message.Request.Headers);

        await ProcessNextAsync(message, pipeline, currentIndex).ConfigureAwait(false);
    }

    private static void RemoveDuplicateHeaderValues(PipelineRequestHeaders headers)
    {
        if (headers.TryGetValues(AuthorizationHeaderName, out var headerValues)
            && headerValues is not null
            && headerValues.TryGetNonEnumeratedCount(out var count)
            && count > 1)
        {
            headers.Set(AuthorizationHeaderName, headerValues.First());
        }
    }
}

This can be added to the policies on the AzureOpenAIClientOptions with the PipelinePosition.PerTry position.

options.AddPolicy(new AuthorizationHeaderWorkaroundPolicy(), PipelinePosition.PerTry);

@mikeharder
Copy link
Member

mikeharder commented Oct 2, 2024

Suspected root cause on L56:

public override async ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int currentIndex)
{
if (message?.Request is not null)
{
if (!IsTokenFresh())
{
TokenRequestContext tokenRequestContext = CreateRequestContext(message.Request);
_currentToken
= await _credential.GetTokenAsync(tokenRequestContext, cancellationToken: default).ConfigureAwait(false);
}
message?.Request?.Headers?.Add("Authorization", $"Bearer {_currentToken.Value.Token}");

I believe the code should use Headers.SetValue() instead of Headers.Add() to avoid duplicate headers, since the same HttpMessage is used on retries.

Above code was recently modified in this PR: #44983

@alexmg
Copy link

alexmg commented Oct 23, 2024

Is there an expected ETA for a release that contains this fix?

Being an upstream dependency of many other projects like Semantic Kernal and Kernel Memory this is causing issues for many downstream consumers.

@annelo-msft
Copy link
Member

annelo-msft commented Oct 23, 2024

@trrwilson, would you be able to share plans regarding the next release of the Azure.AI.OpenAI library?

dluc added a commit to microsoft/kernel-memory that referenced this issue Oct 31, 2024
## Motivation and Context (Why the change? What's the scenario?)

Temporary workaround for Azure OpenAI SDK bug - service throttling
handled incorrectly and causing incorrect HTTP status error

See:
* Azure/azure-sdk-for-net#46109
* microsoft/semantic-kernel#8929
* #855
dluc added a commit to microsoft/kernel-memory that referenced this issue Nov 1, 2024
…sts (#876)

## Motivation and Context (Why the change? What's the scenario?)

When retrying requests after throttling, the Azure OpenAI SDK is sending
malformed requests, with multiple `Authorization` headers, which are
rejected by the Azure OpenAI service with a `401 (Unauthorized)` error
code, leading to an exception in the SDK.

See 
- Azure/azure-sdk-for-net#46109
- microsoft/semantic-kernel#8929
- #855

## High level description (Approach, Design)

Inject a policy to fix malformed HTTP headers.

Functional test included, to verify the fix.
@github-actions github-actions bot locked and limited conversation to collaborators Jan 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Client This issue points to a problem in the data-plane of the library. customer-reported Issues that are reported by GitHub users external to the Azure organization. needs-team-attention Workflow: This issue needs attention from Azure service team or SDK team OpenAI question The issue doesn't require a change to the product in order to be resolved. Most issues start as that Service Attention Workflow: This issue is responsible by Azure service team.
Projects
None yet
5 participants