Skip to main content
Question

.Net SDK v10.2.0 custom TokenStorage error when handling a stale access token

  • December 4, 2025
  • 0 replies
  • 8 views

Application that persists User Access and Refresh token works with .Net SDK v10.1.0, fails with :

"The given key 'Retry-After' was not present in the dictionary" error 

at System.ThrowHelper.ThrowKeyNotFoundException[T](T key)
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at Box.Sdk.Gen.BoxRetryStrategy.RetryAfter(FetchOptions fetchOptions, FetchResponse fetchResponse, Int32 attemptNumber)
   at Box.Sdk.Gen.Internal.BoxNetworkClient.<Box-Sdk-Gen-INetworkClient-FetchAsync>d__3.MoveNext()
   at Box.Sdk.Gen.Managers.FoldersManager.<GetFolderByIdAsync>d__8.MoveNext()
   at Program.<<Main>$>d__0.MoveNext() in C:\Users\xxxxx\source\repos\BoxTestTokenStore\Program.cs:line 28

 

A sample console test app code fragments to recreate:

 

Simple Model for token persistence:

namespace BoxTestTokenStore
{
    internal class TokenData
    {
        public string AccessToken { get; set; } = string.Empty;
        public string RefreshToken { get; set; } = string.Empty;

    }
}

Custom Token store code:

using Box.Sdk.Gen;
using Box.Sdk.Gen.Schemas;
using System.Text.Json;
using File = System.IO.File;
using Task = System.Threading.Tasks.Task;

namespace BoxTestTokenStore
{
    internal class JsonFileTokenStore(string filePath) : ITokenStorage
    {
        private bool activeLoaded = false;
        private TokenData ActiveToken { get; set; } = new();
        private AccessToken FromActiveTokenData { get => new() { AccessTokenField = ActiveToken.AccessToken,RefreshToken = ActiveToken.RefreshToken }; }    
       
        
        public async Task ClearAsync()
        {
            ActiveToken = new();
            await StoreAsync(FromActiveTokenData);
        }
        private async Task<bool> LoadAsync()
        {
            if (activeLoaded)
                return true;
            var json = await File.ReadAllTextAsync(filePath);
            var tokenData = JsonSerializer.Deserialize<TokenData>(json);
            if (tokenData is null)
                return false;
            
            ActiveToken = tokenData;
            activeLoaded = true;
            return true;
        }

        public async Task<AccessToken?> GetAsync()
        {
            if (!activeLoaded)
            { 
                // first time load
                var checkLoad = await LoadAsync();
                if (checkLoad == false)
                    throw new Exception("Load failed");
            }

            return FromActiveTokenData;

        }
        public async Task StoreAsync(AccessToken token)
        {
            
            ActiveToken = new() { AccessToken = token.AccessTokenField ?? string.Empty, RefreshToken = token.RefreshToken ?? string.Empty };
            var json = JsonSerializer.Serialize(ActiveToken, new JsonSerializerOptions { WriteIndented = true });
            await File.WriteAllTextAsync(filePath, json);
        }
    }
}

 

Json file:

 

{
  "AccessToken": "VALID_ACCESS_TOKEN",
  "RefreshToken": "VALID_REFRESH_TOKEN"
}

 

Program.cs (top level):

using Box.Sdk.Gen;
using BoxTestTokenStore;
using System.Text.Json;


string clientId = "YOUR_CLIENT_ID";
string clientSecret = "YOUR_CLIENT_SECRET";
string tokenFilePath = "box_oauth_token.json";


string tokenFilePath = "box_oauth_token.json";
var tokenStorage = new JsonFileTokenStore(tokenFilePath);

var config = new OAuthConfig(clientId: clientId, clientSecret: clientSecret, tokenStorage: tokenStorage);
var auth = new BoxOAuth(config: config);
var client = new BoxClient(auth);


// trigger token store token management - proof that tokens are valid will error on v10.2.0 if JSON token is stale.


var root = await client.Folders.GetFolderByIdAsync("0");

 

// generous guess for Access token expires in , could be already stale
long expiresIn =4800;

// Wait for access token to expire (plus a  5 min buffer - to be sure)
await Task.Delay(TimeSpan.FromSeconds(expiresIn + (60*5)));

// repeat trigger token store token management => this fail v10.2.0, passes v10.1.0
root = await client.Folders.GetFolderByIdAsync("0");
Console.WriteLine("Done");