Classify bare HTTP 403 as access-denied for Group/Teams sites
Microsoft 365 Group / Teams-connected sites surface access-denied on some CSOM calls as a raw "(403) FORBIDDEN" WebException carrying 0x80070005 (E_ACCESSDENIED), not as a typed ServerException with ServerErrorTypeName = System.UnauthorizedAccessException. IsAccessDenied only matched the typed shape, so those denials became generic InvalidOperationExceptions the elevation coordinator never caught — no auto-elevation ran and the operation failed even for a SharePoint admin. Walk the inner-exception chain and treat any of these as access-denied: the typed ServerException, a WebException with HTTP 403, or a message containing the E_ACCESSDENIED HRESULT. Per-site dedupe still caps elevation to one retry, so a 403 elevation cannot fix (policy/endpoint block) won't loop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -84,10 +84,29 @@ public static class ExecuteQueryRetryHelper
|
|||||||
return new InvalidOperationException(enriched, ex);
|
return new InvalidOperationException(enriched, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsAccessDenied(Exception ex) =>
|
// Access-denied reaches us in two shapes: a typed CSOM ServerException
|
||||||
ex is ServerUnauthorizedAccessException ||
|
// (ServerErrorTypeName = System.UnauthorizedAccessException), and — notably on
|
||||||
(ex is ServerException se &&
|
// Microsoft 365 Group / Teams-connected sites — a bare HTTP (403) FORBIDDEN
|
||||||
string.Equals(se.ServerErrorTypeName, "System.UnauthorizedAccessException", StringComparison.Ordinal));
|
// WebException carrying "Access is denied ... 0x80070005 (E_ACCESSDENIED)".
|
||||||
|
// Both are ownership issues elevation can fix, so classify either as access-denied.
|
||||||
|
private static bool IsAccessDenied(Exception ex)
|
||||||
|
{
|
||||||
|
for (Exception? cur = ex; cur is not null; cur = cur.InnerException)
|
||||||
|
{
|
||||||
|
if (cur is ServerUnauthorizedAccessException)
|
||||||
|
return true;
|
||||||
|
if (cur is ServerException se &&
|
||||||
|
string.Equals(se.ServerErrorTypeName, "System.UnauthorizedAccessException", StringComparison.Ordinal))
|
||||||
|
return true;
|
||||||
|
if (cur is WebException we && we.Response is HttpWebResponse resp &&
|
||||||
|
resp.StatusCode == HttpStatusCode.Forbidden)
|
||||||
|
return true;
|
||||||
|
if (cur.Message.Contains("0x80070005", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
cur.Message.Contains("E_ACCESSDENIED", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
internal static bool IsThrottleException(Exception ex)
|
internal static bool IsThrottleException(Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user