Module version: 1.5.0
OS/PowerShell version: Windows, PowerShell 5.1/7.x with German locale (de-DE)
Description:
Test-AccessToken always returns $false on systems with a non-English Windows locale, causing every Graph API call to attempt a silent token refresh, which then fails with:
"Cannot validate argument on parameter 'ClientID'. The argument is null or empty."
Root cause:
In Test-AccessToken.ps1, the ExpiresOn property is converted to a string using the
system's local culture (e.g. "03.04.2026" in de-DE = DD.MM.YYYY), but then parsed
back using InvariantCulture which expects MM.DD.YYYY — interpreting April 3rd as
March 4th, putting the expiry ~30 days in the past.
Problematic line:
$ExpiresOnUTC = [DateTimeOffset]::Parse(
$Global:AccessToken.ExpiresOn.ToString(),
[System.Globalization.CultureInfo]::InvariantCulture,
[System.Globalization.DateTimeStyles]::AssumeUniversal
).ToUniversalTime()
Suggested Fix (with a little help of AI, but this worked for me! Couldnt get it to work otherwise):
Use expires_in (seconds) from the token object to calculate expiry instead of
re-parsing the ExpiresOn string:
if ($Global:AccessToken.PSObject.Properties["expires_in"] -and $Global:AccessToken.expires_in) {
$ExpiresOnUTC = [DateTimeOffset]::UtcNow.AddSeconds($Global:AccessToken.expires_in)
} else {
$ExpiresOnUTC = [DateTimeOffset]::Parse($Global:AccessToken.ExpiresOn.ToString(),
[System.Globalization.CultureInfo]::CurrentCulture,
[System.Globalization.DateTimeStyles]::AssumeLocal).ToUniversalTime()
}
Module version: 1.5.0
OS/PowerShell version: Windows, PowerShell 5.1/7.x with German locale (de-DE)
Description:
Test-AccessToken always returns $false on systems with a non-English Windows locale, causing every Graph API call to attempt a silent token refresh, which then fails with:
"Cannot validate argument on parameter 'ClientID'. The argument is null or empty."
Root cause:
In Test-AccessToken.ps1, the ExpiresOn property is converted to a string using the
system's local culture (e.g. "03.04.2026" in de-DE = DD.MM.YYYY), but then parsed
back using InvariantCulture which expects MM.DD.YYYY — interpreting April 3rd as
March 4th, putting the expiry ~30 days in the past.
Problematic line:
$ExpiresOnUTC = [DateTimeOffset]::Parse(
$Global:AccessToken.ExpiresOn.ToString(),
[System.Globalization.CultureInfo]::InvariantCulture,
[System.Globalization.DateTimeStyles]::AssumeUniversal
).ToUniversalTime()
Suggested Fix (with a little help of AI, but this worked for me! Couldnt get it to work otherwise):
Use expires_in (seconds) from the token object to calculate expiry instead of
re-parsing the ExpiresOn string:
if ($Global:AccessToken.PSObject.Properties["expires_in"] -and $Global:AccessToken.expires_in) {
$ExpiresOnUTC = [DateTimeOffset]::UtcNow.AddSeconds($Global:AccessToken.expires_in)
} else {
$ExpiresOnUTC = [DateTimeOffset]::Parse($Global:AccessToken.ExpiresOn.ToString(),
[System.Globalization.CultureInfo]::CurrentCulture,
[System.Globalization.DateTimeStyles]::AssumeLocal).ToUniversalTime()
}