Hi @stephane.godbout
Welcome to the Auth0 Community.
You are building an ASP.NET Core security controller that calls the Auth0 Management API using the IManagementApiClient (version 10.0.0) with Machine-to-Machine (M2M) authentication. When you pass a valid Auth0 user ID, the API returns the user data correctly. When you pass an invalid ID, Auth0 returns a 404 error, but you are unable to catch it as an ErrorApiException with the status code. Instead, you are catching it as a generic Exception with the message "The user does not exist."
Your code structure is correct — ErrorApiException is the right exception type to catch. However, your catch block ordering may be preventing the ErrorApiException from being caught properly, or the exception is being thrown but the StatusCode property is not being populated as expected.
[Root Cause]
The issue is likely one of the following:
- Catch block ordering is incorrect: In C#, catch blocks are evaluated in order from top to bottom. If you have a
catch (Exception ex) block before the catch (ErrorApiException ex) block, the generic Exception block will catch the ErrorApiException first (since ErrorApiException inherits from Exception), and the more specific block will never execute.
- The
StatusCode property is null or not set: In some versions of the Auth0.ManagementApi SDK, the StatusCode property may not be populated for all error responses. You should verify that ex.StatusCode is not null before using it.
- The exception is being thrown from a different source: If the
GetAsync() method is not throwing ErrorApiException directly, it may be wrapped in another exception type.
[Solution]
Step 1: Verify catch block ordering
Ensure that your catch (ErrorApiException ex) block comes before any catch (Exception ex) blocks. In C#, catch blocks are evaluated in order, and the first matching block is executed. Since ErrorApiException inherits from Exception, a generic Exception catch block will intercept it if placed first.
Correct order:
try
{
GetUserResponseContent result = await _managementApiClient.Users.GetAsync(
id: auth0id,
request: new GetUserRequestParameters { IncludeFields = true }
);
// ... rest of code
}
catch (ErrorApiException ex) // MOST SPECIFIC - catch this first
{
System.Net.HttpStatusCode statusCode = ex.StatusCode;
_logger.LogError("Auth0 API error: {StatusCode} - {Message}", statusCode, ex.Message);
return NotFound(ex.Message);
}
catch (Exception ex) // LEAST SPECIFIC - catch this last
{
_logger.LogError("Unexpected error: {Message}", ex.Message);
return BadRequest();
}
Step 2: Verify the StatusCode property is populated
Add logging to confirm that ex.StatusCode is not null and contains the expected HTTP status code:
catch (ErrorApiException ex)
{
_logger.LogError("ErrorApiException caught");
_logger.LogError("StatusCode: {StatusCode}", ex.StatusCode);
_logger.LogError("Message: {Message}", ex.Message);
_logger.LogError("ApiError: {ApiError}", ex.ApiError);
// Check if StatusCode is null
if (ex.StatusCode == null)
{
_logger.LogWarning("StatusCode is null; using Message instead");
return NotFound(ex.Message);
}
// Use StatusCode for more granular error handling
if (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return NotFound("User not found");
}
else if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
return Unauthorized("Invalid credentials");
}
else if (ex.StatusCode == System.Net.HttpStatusCode.BadRequest)
{
return BadRequest(ex.Message);
}
return StatusCode((int)ex.StatusCode, ex.Message);
}
Step 3: Inspect the ApiError property for additional details
The ErrorApiException also has an ApiError property that contains structured error information from Auth0:
catch (ErrorApiException ex)
{
_logger.LogError("StatusCode: {StatusCode}", ex.StatusCode);
_logger.LogError("Message: {Message}", ex.Message);
if (ex.ApiError != null)
{
_logger.LogError("ApiError.Error: {Error}", ex.ApiError.Error);
_logger.LogError("ApiError.ErrorDescription: {ErrorDescription}", ex.ApiError.ErrorDescription);
}
if (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return NotFound(ex.ApiError?.ErrorDescription ?? ex.Message);
}
return StatusCode((int)(ex.StatusCode ?? System.Net.HttpStatusCode.InternalServerError), ex.Message);
}
Step 4: Remove the redundant generic Exception catch block
Since you are now catching ErrorApiException with proper status code handling, you can simplify your exception handling:
[HttpGet("User/{auth0id}")]
[Authorize(Policy = "Read:Security_Tables")]
public async Task<ActionResult<UserModel>> GetUser(string auth0id)
{
_logger.LogInformation("{UserId} - Get: api/Security/User/{auth0id}",
User.FindFirst(ClaimTypes.NameIdentifier)!.Value, auth0id);
try
{
GetUserResponseContent result = await _managementApiClient.Users.GetAsync(
id: auth0id,
request: new GetUserRequestParameters { IncludeFields = true }
);
UserModel user = new UserModel
{
User_Id = result.UserId,
Email = result.Email!,
Email_Verified = result.EmailVerified ?? false,
Username = result.Username,
Phone_Number = result.PhoneNumber,
Name = result.Name!,
Nickname = result.Nickname,
Given_Name = result.GivenName!,
Family_Name = result.FamilyName!,
Blocked = result.Blocked ?? false
};
return Ok(user);
}
catch (ErrorApiException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
_logger.LogWarning("User not found: {Auth0Id}", auth0id);
return NotFound($"User {auth0id} not found");
}
catch (ErrorApiException ex) when (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
_logger.LogError("Unauthorized: Invalid M2M credentials");
return Unauthorized("Invalid credentials");
}
catch (ErrorApiException ex)
{
_logger.LogError("Auth0 API error: {StatusCode} - {Message}", ex.StatusCode, ex.Message);
return StatusCode((int)(ex.StatusCode ?? System.Net.HttpStatusCode.InternalServerError), ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error retrieving user");
return BadRequest("An unexpected error occurred");
}
}
Step 5: Verify M2M token and permissions
If ErrorApiException is still not being thrown, verify that your M2M authentication is working correctly:
try
{
// Test the connection by making a simple API call
var testUser = await _managementApiClient.Users.GetAsync("test-invalid-id");
}
catch (ErrorApiException ex)
{
_logger.LogError("M2M test - StatusCode: {StatusCode}, Message: {Message}",
ex.StatusCode, ex.Message);
}
If this test call also fails to throw ErrorApiException, there may be a version-specific issue with Auth0.ManagementApi v10.0.0.
Why StatusCode May Be Null:
The StatusCode property is of type System.Net.HttpStatusCode? (nullable). It may be null if:
- The exception was thrown before the HTTP response was received
- The SDK version has a bug where
StatusCode is not being populated
- The exception is being thrown from a different layer (e.g., network error, serialization error)
Always check for null before using ex.StatusCode.
Version Consideration:
You are using Auth0.ManagementApi v10.0.0. If the issue persists after following these steps, consider:
- Checking the release notes for v10.0.0 to see if there are known issues with exception handling
- Testing with a different version (e.g., v9.x) to see if the issue is version-specific
- Contacting Auth0 Support with your exact code and the output of the logging statements above
We hope this resolves the issue. Please follow up with the diagnostic information from Step 2 if the problem persists.
Kind regards,
Nik