Skip to main content

API Error Handling & Diagnostics

ResQueServe provides rich, machine-readable debugging context beyond RFC 7807

Updated over a month ago

ResQueServe moves beyond simple HTTP Status Codes (like 404 or 500) to provide rich, machine-readable debugging context. We strictly adhere to the RFC 7807 (Problem Details for HTTP APIs) standard.

When an API request fails, the server returns a structured JSON document known as a "Problem Detail". This document tells you not just that an error occurred, but why, where, and how to fix it.


Anatomy of a Failure Response

If you inspect the raw HTTP response of a failed request (e.g., trying to create a Unit with a duplicate Callsign), you will see a payload like this:

JSON

{
"title": "The request was invalid.",
"status": 400,
"detail": "Invalid value for property 'CallNumberEmergency'. Provided: '911-better-call-saul'.",
"instance": "/api/v1/info/system",
"traceIdentifier": "0HNIFIC46DOJN:00000001",
"exceptionType": "XENBIT.ResQueServe.Core.Exceptions.InvalidValueException",
"exception": {
"invalidProperty": "CallNumberEmergency",
"providedValue": "911-better-call-saul",
"message": "Invalid value for property 'CallNumberEmergency'. Provided: '911-better-call-saul'.",
"problems": [76]
},
"cooked": ["Das Format der Notrufnummer der Leitstelle ist ungültig."]
}
  • traceIdentifier: A unique ID for the specific request. Always log this. It is the primary key our support team uses to look up server-side logs.

  • cooked: An array of human-readable, pre-localized strings. These are safe to display directly to an end-user (e.g., in a Toast notification).

  • problems: An array of integer codes corresponding to the ProblemInformation enum. This allows your code to react programmatically to specific errors without parsing text.

  • exceptionType: The fully qualified name of the server-side exception.


SDK Exception Hierarchy

The ResQueServe SDK (XENBIT.ResQueServe.SDK) automatically intercepts these JSON responses and deserializes them into strongly-typed C# exceptions. All platform-specific exceptions inherit from the base class ResQueServeException. This allows you to write a single catch block for general error handling, or specific blocks for granular control.

Core Exception Types

Exception Class

HTTP Status

Trigger Condition

Key Properties

DataNotFoundException

404

You requested a resource ID that doesn't exist, or a referenced parent (like a Station) is missing.

LookupProperty: The field searched.


ProvidedValue: The ID that failed.

DuplicateException

409

A unique constraint was violated (e.g., creating two units with the same Callsign).

DuplicateProperty: The field in conflict.


ProvidedValue: The conflicting data.

ValidationException

400

The data violates business rules (e.g., assigning a Helicopter to a Hospital that has no helipad).

Problems: Array of ProblemInformationcodes.

IncompleteRequestException

400

A required field (like Name or Category) was null or missing.

MissingProperty: Name of the missing field.

InvalidValueException

400

Data format is wrong (e.g., invalid Regex for a Callsign, or coordinates out of range).

InvalidProperty: The specific field.


ProvidedValue: The rejected input.

UnknownException

500 / Other

Something unexpected happened (Network failure, Serialization error, or unmapped API error).

StatusCode: The raw HTTP status.


ProblemDetails: The raw JSON object.


The ProblemInformation Enum Strategy

While exception types give you the category of error, the ProblemInformation enum gives you the specific cause. This is critical for building robust integrations that can self-correct or guide the user. These enums are defined in XENBIT.ResQueServe.Abstractions.DataStructure.


Practical Implementation Patterns

Do not rely on try/catch (Exception ex). That is too generic. Below is the recommended pattern for a production-grade integration.

Scenario: Creating a New Unit

This example attempts to create a unit and handles specific edge cases like "Station not found" or "Duplicate Callsign" distinct from a general crash.

C#

using XENBIT.ResQueServe.Abstractions.DataStructures;
using XENBIT.ResQueServe.Core.Exceptions;
using XENBIT.ResQueServe.Abstractions.Exceptions;

public async Task SafeCreateUnit(long dpcId, IUnit newUnit)
{
try
{
var result = await _unitClient.CreateAsync(dpcId, newUnit);
Console.WriteLine($"Success! Unit created with ID {result.Id}");
}
// 1. Handle "Missing Data" specifically
catch (DataNotFoundException ex)
{
// Example: The POI ID provided for the station doesn't exist
if (ex.LookupProperty == "Poi")
{
Console.WriteLine($"Error: The Station ID {ex.ProvidedValue} does not exist.");
}
else
{
Console.WriteLine($"Error: {ex.Message}");
}
}
// 2. Handle "Validation Rules"
catch (ValidationException ex)
{
// Check for specific logic problems using the Enum
if (ex.Problems.Contains(ProblemInformation.CallsignAlreadyExists))
{
Console.WriteLine("That callsign is taken. Please generate a new one.");
}
else if (ex.Problems.Contains(ProblemInformation.UnitStationPoiIsRequired))
{
Console.WriteLine("You forgot to assign a Home Station.");
}
else
{
// Fallback: Show the server's human-readable messages
// The 'Cooked' property is not on the Exception directly, but usually
// the Message property of these exceptions is composed of the title.
// For full access to 'Cooked', we check the inner ProblemDetails if exposed.
Console.WriteLine("Validation Failed: " + ex.Message);
}
}
// 3. Handle Formatting/Regex errors
catch (InvalidValueException ex)
{
Console.WriteLine($"The value '{ex.ProvidedValue}' is invalid for field '{ex.InvalidProperty}'.");
}
// 4. Catch-all for Platform Errors
catch (ResQueServeException ex)
{
// LOGGING THE TRACE ID IS CRITICAL
// It's usually inside the Problems array or the inner ProblemDetails
var traceId = ex.Problems?.FirstOrDefault().ToString() ?? "N/A";
_logger.LogError(ex, "Platform Error. TraceId: {TraceId}", traceId);
}
}

Frontend & UI Integration

If you are building a UI (Blazor, WPF, or MVC) that consumes the SDK, the exceptions include a helper method HandleDispatcherExceptionOnFrontend. While this is primarily for internal internal views, the ProblemDetails.Cooked property is designed for you.

Example: Displaying errors in a UI

When you catch a ResQueServeException, you should look at the internal ProblemDetails.

C#

catch (ResQueServeException ex) when (ex is UnknownException unknownEx)
{
// UnknownException wraps the raw ProblemDetails
if (unknownEx.ProblemDetails?.Cooked != null)
{
foreach (var userMessage in unknownEx.ProblemDetails.Cooked)
{
MyToastService.ShowError(userMessage);
}
}
}

Debugging & Support

When reporting an issue to ResQueServe support, "It didn't work" is rarely enough. Because our system is distributed, we rely on the Trace Identifier.

  1. Catch ResQueServeException.

  2. Inspect the JSON response (or the Exception properties).

  3. Extract the traceIdentifier (e.g., 00-8432b7a1...).

  4. Send this ID along with the timestamp to developer support.

This ID allows us to replay the exact request context in our distributed logs to see exactly why the validation logic rejected your specific payload.

Did this answer your question?