fix: MOS token cache timezone comparison bug in TryGetCachedToken#278
Merged
sellakumaran merged 2 commits intomicrosoft:mainfrom Feb 20, 2026
Merged
Conversation
…icrosoft#277) Fix DateTime timezone comparison in TryGetCachedToken that caused expired MOS tokens to be reused on machines with timezones ahead of UTC (IST, JST, AEST, etc.). The bug caused persistent HTTP 401 errors from titles.prod.mos.microsoft.com after the initial token expired. Root cause: DateTime.TryParse with UTC 'Z' suffix converts to local time by default, then raw tick comparison against DateTime.UtcNow skips timezone conversion - making expired tokens appear valid for N extra hours where N equals the UTC offset. Changes: - MosTokenService.TryGetCachedToken: Add CultureInfo.InvariantCulture and DateTimeStyles.AdjustToUniversal to DateTime.TryParse to ensure the parsed expiry stays in UTC for correct comparison with DateTime.UtcNow - PublishCommand 401 troubleshooting: Show full cache file path (~/.a365/mos-token-cache.json) instead of just the filename, so users can find and delete the correct cache file - Add 6 regression tests (MosTokenServiceCacheTests) covering: valid cached token, expired cached token, UTC timezone parsing, 2-minute safety buffer, cross-environment isolation, missing cache file Fixes microsoft#277
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a critical timezone-related bug in MOS token caching that caused persistent HTTP 401 errors on machines with timezones ahead of UTC. The bug occurred because DateTime.TryParse was converting UTC timestamps to local time before comparison with DateTime.UtcNow, making expired tokens appear valid for extra hours equal to the UTC offset. The PR also improves the 401 error troubleshooting guidance by displaying the full cache file path.
Changes:
- Fixed DateTime timezone comparison bug in
TryGetCachedTokenby addingCultureInfo.InvariantCultureandDateTimeStyles.AdjustToUniversalto ensure parsed timestamps remain in UTC - Updated 401 error message to show the full cache path (
~/.a365/mos-token-cache.json) instead of just the filename - Added 6 comprehensive regression tests for token cache validation, timezone parsing, expiry buffer, and environment isolation
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
MosTokenService.cs |
Fixed DateTime.TryParse to use DateTimeStyles.AdjustToUniversal, preventing timezone conversion that caused expired tokens to appear valid |
PublishCommand.cs |
Updated 401 error message to display full cache file path for better user guidance |
MosTokenServiceCacheTests.cs |
Added new test file with 6 regression tests covering cache validity, timezone parsing bug, 2-minute buffer, and cross-environment isolation |
Add TryParseUtcTimestamp_WithAdjustToUniversal test to ensure UTC "Z" timestamps are parsed as DateTimeKind.Utc, preventing timezone-related token cache bugs from regressing undetected in CI. Clarify comments in existing regression test. Mark MosTokenServiceTests with [Collection("MosTokenCacheTests")] for test coordination.
sellakumaran
approved these changes
Feb 20, 2026
ajmfehr
approved these changes
Feb 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fix MOS token cache timezone comparison bug and improve 401 error guidance. Fixes #277.
Problem
Two bugs in
a365 publishthat together cause persistent HTTP 401 errors on machines with timezones ahead of UTC:Bug 1: DateTime timezone mismatch in
TryGetCachedTokenDateTime.TryParsewith a UTC "Z" suffix converts the timestamp to local time by default, then the comparison withDateTime.UtcNowuses raw tick values without timezone conversion. This makes expired tokens appear valid for extra hours equal to the UTC offset:Bug 2: Cache file location mismatch in error guidance
The 401 troubleshooting message said
"Delete: .mos-token-cache.json"without the full path. The cache lives at~/.a365/mos-token-cache.jsonbut users look in%LOCALAPPDATA%\Microsoft.Agents.A365.DevTools.Cli\where other CLI config files are stored.Changes
MosTokenService.csCultureInfo.InvariantCulture+DateTimeStyles.AdjustToUniversaltoDateTime.TryParseso parsed expiry stays in UTCPublishCommand.cs~/.a365/mos-token-cache.json) in 401 troubleshooting messageMosTokenServiceCacheTests.csTesting
DateTime.TryParseinstances in the codebase need this fix (checked)AcquireTokenAsync_CachedTokenUtcZSuffix_ParsedAsUtcNotLocalTime) writes a token expired 3 hours ago in UTC — with the old code, IST machines would still treat it as valid for ~2.5 more hoursCode Review Checklist
IDisposableproperly disposed (test class implementsIDisposable, restores cache file)DisableParallelizationpattern (shared cache file)