Skip to content

Comments

fix: MOS token cache timezone comparison bug in TryGetCachedToken#278

Merged
sellakumaran merged 2 commits intomicrosoft:mainfrom
pratapladhani:fix/mos-token-cache-timezone-comparison
Feb 20, 2026
Merged

fix: MOS token cache timezone comparison bug in TryGetCachedToken#278
sellakumaran merged 2 commits intomicrosoft:mainfrom
pratapladhani:fix/mos-token-cache-timezone-comparison

Conversation

@pratapladhani
Copy link
Contributor

Summary

Fix MOS token cache timezone comparison bug and improve 401 error guidance. Fixes #277.

Problem

Two bugs in a365 publish that together cause persistent HTTP 401 errors on machines with timezones ahead of UTC:

Bug 1: DateTime timezone mismatch in TryGetCachedToken

DateTime.TryParse with a UTC "Z" suffix converts the timestamp to local time by default, then the comparison with DateTime.UtcNow uses raw tick values without timezone conversion. This makes expired tokens appear valid for extra hours equal to the UTC offset:

Timezone Extra hours token appears "valid"
IST (+5:30) ~5.5 hours
JST (+9:00) ~9 hours
AEST (+10:00) ~10 hours

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.json but users look in %LOCALAPPDATA%\Microsoft.Agents.A365.DevTools.Cli\ where other CLI config files are stored.

Changes

File Change
MosTokenService.cs Add CultureInfo.InvariantCulture + DateTimeStyles.AdjustToUniversal to DateTime.TryParse so parsed expiry stays in UTC
PublishCommand.cs Show full cache path (~/.a365/mos-token-cache.json) in 401 troubleshooting message
MosTokenServiceCacheTests.cs 6 new regression tests covering cache validity, timezone parsing, buffer, and cross-environment isolation

Testing

  • 20/20 MosTokenService tests pass (14 existing + 6 new)
  • Build: 0 warnings, 0 errors
  • No other DateTime.TryParse instances in the codebase need this fix (checked)
  • Key regression test (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 hours

Code Review Checklist

  • No "Kairo" keyword references
  • Microsoft copyright headers on all files
  • IDisposable properly disposed (test class implements IDisposable, restores cache file)
  • Cross-platform compatible (no Windows-specific paths)
  • No secrets or hardcoded values
  • Tests use DisableParallelization pattern (shared cache file)

…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
Copilot AI review requested due to automatic review settings February 19, 2026 04:35
@pratapladhani pratapladhani requested review from a team as code owners February 19, 2026 04:35
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 TryGetCachedToken by adding CultureInfo.InvariantCulture and DateTimeStyles.AdjustToUniversal to 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 sellakumaran enabled auto-merge (squash) February 20, 2026 02:20
@sellakumaran sellakumaran merged commit 8cf58e1 into microsoft:main Feb 20, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: a365 publish reuses expired MOS tokens due to DateTime timezone comparison in TryGetCachedToken

3 participants