Fehlerbehandlung
Dieses Kapitel beschreibt, wie Fehler in GraphQL-Antworten zurückgegeben werden und wie sie behandelt werden.
Struktur der Fehlerantwort
GraphQL-Fehler werden im Array errors der Antwort zurückgegeben. Jeder Fehler enthält:
{
"errors": [
{
"message": "Entity not found",
"locations": [
{
"line": 3,
"column": 5
}
],
"path": ["runtime", "industryEnergyEnergyMeter", "items"],
"extensions": {
"code": "ENTITY_NOT_FOUND",
"details": "No entity found with rtId: 65dc6d24cc529cdc46c84fcc"
}
}
],
"data": null
}
Fehlerfelder
| Feld | Beschreibung |
|---|---|
message | Für Menschen lesbare Fehlerbeschreibung |
locations | Position in der GraphQL-Query, an der der Fehler aufgetreten ist |
path | Pfad zu dem Feld, das den Fehler verursacht hat |
extensions.code | Maschinenlesbarer Fehlercode |
extensions.details | Zusätzliche Fehlerdetails |
Partielle Antworten
GraphQL kann partielle Daten zusammen mit Fehlern zurückgeben. Wenn einige Teile einer Query erfolgreich sind, während andere fehlschlagen, können Sie sowohl data als auch errors erhalten:
{
"errors": [
{
"message": "Access denied to field 'configuration'",
"path": ["runtime", "systemCommunicationAdapter", "items", 0, "configuration"]
}
],
"data": {
"runtime": {
"systemCommunicationAdapter": {
"items": [
{
"rtId": "65d5c447b420da3fb12381bc",
"name": "My Adapter",
"configuration": null
}
]
}
}
}
}
Häufige Fehlercodes
Query-Fehler
| Code | Beschreibung |
|---|---|
ENTITY_NOT_FOUND | Die angeforderte Entität existiert nicht |
TYPE_NOT_FOUND | Die angegebene ckTypeId ist ungültig |
INVALID_FILTER | Filterparameter sind ungültig |
INVALID_SORT | Sortierparameter sind ungültig |
INVALID_CURSOR | Der Paginierungs-Cursor ist ungültig oder abgelaufen |
Mutations-Fehler
| Code | Beschreibung |
|---|---|
VALIDATION_ERROR | Die Eingabedaten haben die Validierung nicht bestanden |
REQUIRED_FIELD_MISSING | Ein erforderliches Feld wurde nicht angegeben |
INVALID_FIELD_VALUE | Der Feldwert entspricht nicht dem erwarteten Typ oder Format |
ENTITY_ALREADY_EXISTS | Eine Entität mit dem angegebenen Identifier existiert bereits |
REFERENCE_NOT_FOUND | Die referenzierte Entität (z. B. in einer Assoziation) wurde nicht gefunden |
CONSTRAINT_VIOLATION | Die Operation verletzt eine Bedingung (z. B. Multiplizität) |
Autorisierungsfehler
| Code | Beschreibung |
|---|---|
UNAUTHORIZED | Authentifizierung erforderlich |
FORBIDDEN | Unzureichende Berechtigungen für die Operation |
ACCESS_DENIED | Zugriff auf eine bestimmte Ressource verweigert |
BLUEPRINT_LOCKED | Die Entität ist durch einen Blueprint gesperrt und kann von Benutzern nicht geändert oder gelöscht werden, siehe Durch Blueprint gesperrte Entitäten |
Durch Blueprint gesperrte Entitäten
Ein Blueprint kann produktseitige Entitäten ausliefern, zum Beispiel die Kategorisierungsregeln, auf die sich ein
Buchhaltungs-Blueprint stützt. Für einen CK-Typ, der durch eine Datenrichtlinie mit ProtectBlueprintLocked geschützt ist
(siehe Schreibgeschützt für Benutzer),
kann ein Benutzer eine solche Entität nicht mehr ändern oder löschen. Der Schreibzugriff wird mit dem stabilen Fehlercode
BLUEPRINT_LOCKED und der Engine-Meldungsnummer 6384 abgewiesen. Die Entität bleibt unverändert; Lesezugriffe sind nie
betroffen.
GraphQL
Der Fehler wird für die Mutationen create, update und delete zurückgegeben. Die extensions enthalten alles, was ein
Client braucht, um „vom Produkt gesperrt“ statt eines allgemeinen Fehlers anzuzeigen:
{
"errors": [
{
"message": "Access denied: entity 'Accounting-1.0.0/CategorizationRule-1@65dc6d24cc529cdc46c84fcc' is locked by blueprint and cannot be changed.",
"path": ["runtime", "runtimeEntities", "update"],
"extensions": {
"code": "BLUEPRINT_LOCKED",
"messageNumber": 6384,
"ckTypeId": "Accounting-1.0.0/CategorizationRule-1",
"rtId": "65dc6d24cc529cdc46c84fcc",
"reason": "EntityLocked",
"items": [
{
"ckTypeId": "Accounting-1.0.0/CategorizationRule-1",
"rtId": "65dc6d24cc529cdc46c84fcc",
"reason": "EntityLocked"
}
]
}
}
],
"data": null
}
| Extension | Beschreibung |
|---|---|
code | Immer BLUEPRINT_LOCKED. Prüfen Sie diesen Wert, nicht die Meldung. |
messageNumber | Die Meldungsnummer der Engine, 6384. |
ckTypeId, rtId | Die erste abgewiesene Entität. rtId ist die von der Engine vergebene ID eines abgewiesenen Inserts. |
reason | EntityLocked: Die gespeicherte Entität hat rtBlueprintLocked = true und kann nicht aktualisiert, ersetzt oder gelöscht werden. ProtectedAttributes: Die Änderung setzt oder ändert rtBlueprintLocked, rtBlueprintSource oder rtBlueprintAppliedAt, die auf geschützten Typen nur Blueprints verwalten dürfen. |
items | Je ein Eintrag (ckTypeId, rtId, reason) für jede abgewiesene Entität der Anfrage. |
Eine Mutation wird als Ganzes angewendet: Ist eine Entität eines Batches gesperrt, wird nichts des Batches geschrieben, auch nicht die Entitäten, die nicht gesperrt sind. Entfernen Sie die gesperrten Entitäten aus der Anfrage und senden Sie sie erneut.
REST
Eine REST-Aktion, die die Abweisung weitergibt, antwortet mit dem HTTP-Status 403 Forbidden und
application/problem+json (RFC 9457), das denselben stabilen code trägt:
{
"type": "https://schemas.meshmakers.cloud/errors/blueprint-locked",
"title": "Entity is locked by a blueprint",
"status": 403,
"detail": "Access denied: entity 'Accounting-1.0.0/CategorizationRule-1@65dc6d24cc529cdc46c84fcc' is locked by blueprint and cannot be changed.",
"code": "BLUEPRINT_LOCKED",
"messageNumber": 6384,
"ckTypeId": "Accounting-1.0.0/CategorizationRule-1",
"rtId": "65dc6d24cc529cdc46c84fcc",
"reason": "EntityLocked",
"items": [
{ "ckTypeId": "Accounting-1.0.0/CategorizationRule-1", "rtId": "65dc6d24cc529cdc46c84fcc", "reason": "EntityLocked" }
]
}
Runtime-Entitäten werden über GraphQL geschrieben; die REST-Oberfläche des Asset Repository hat keinen direkten
Entitäts-Schreibzugriff. Der Import-Job (POST {tenantId}/v1/Models/ImportRt) wendet dieselbe Regel mit derselben
Meldungsnummer 6384 an, wenn er im Namen des auslösenden Benutzers läuft: Eine Datei, die eine gesperrte Entität
überschreiben würde, schlägt als Ganzes fehl, das Job-Ergebnis listet jede betroffene ckTypeId@rtId, und es wird nichts
geschrieben (AB#6392). Der auslösende Aufrufer wird mit dem Import-Job mitgeführt; für diesen Schutz ist daher keine
zusätzliche Beschränkung von ImportRt auf Administratoren nötig.
Wer betroffen ist
| Aufrufer | Gesperrte Entität eines geschützten Typs |
|---|---|
| Mandanten-Benutzer, auch mit Schreibberechtigung für den Typ | Abgewiesen mit BLUEPRINT_LOCKED |
Pipeline-Knoten mit Identity: ServiceAccount | Abgewiesen: Ein konfiguriertes Service-Konto wird wie ein Benutzer behandelt. Eine Pipeline, die produktseitige Entitäten schreiben muss, benötigt Identity: System |
System-Kontext: Blueprint-Installation, -Update und erzwungenes erneutes Anwenden, Identity: System-Pipelines, interne Dienste | Erlaubt |
Entsperrte Entität (rtBlueprintLocked ist false oder fehlt) oder ein nicht geschützter Typ | Erlaubt, nichts ändert sich |
Läuft die Datenrichtlinie im Modus AuditOnly, gelingt die Änderung, und statt des Fehlers wird das Audit-Ereignis
DataPermissions.BlueprintLockViolation veröffentlicht.
Fehler im Code behandeln
TypeScript/JavaScript-Beispiel
const response = await client.query({ query: GET_ENERGY_METERS });
if (response.errors) {
for (const error of response.errors) {
console.error(`Error: ${error.message}`);
if (error.extensions?.code === 'ENTITY_NOT_FOUND') {
// Handle missing entity
} else if (error.extensions?.code === 'FORBIDDEN') {
// Handle permission error
} else if (error.extensions?.code === 'BLUEPRINT_LOCKED') {
// The entity is owned by the product: show it read-only
console.warn(`Locked by blueprint: ${error.extensions.rtId}`);
}
}
}
if (response.data) {
// Process successful data
}
Auf partiellen Erfolg prüfen
const response = await client.mutate({
mutation: CREATE_ENTITIES,
variables: { entities: [...] }
});
// Check if any entities were created despite errors
const createdEntities = response.data?.runtime?.myEntities?.create ?? [];
const failedCount = entities.length - createdEntities.length;
if (response.errors && failedCount > 0) {
console.warn(`${failedCount} entities failed to create`);
}
Validierungsfehler
Validierungsfehler enthalten typischerweise Details darüber, welches Feld fehlgeschlagen ist und warum:
{
"errors": [
{
"message": "Validation failed",
"extensions": {
"code": "VALIDATION_ERROR",
"validationErrors": [
{
"field": "voltage",
"message": "Value must be between 0 and 500"
},
{
"field": "name",
"message": "Name must not be empty"
}
]
}
}
]
}
Best Practices
- Prüfen Sie stets auf Fehler: Selbst erfolgreich aussehende Antworten können partielle Fehler enthalten
- Verwenden Sie Fehlercodes: Gleichen Sie mit
extensions.codeab, anstatt Fehlermeldungen zu parsen - Behandeln Sie partielle Daten: Verarbeiten Sie alle gültigen Daten, die zusammen mit Fehlern zurückgegeben werden
- Protokollieren Sie Fehlerdetails: Nehmen Sie
pathundextensionszur Fehlersuche in die Logs auf - Benutzerfreundliche Meldungen: Bilden Sie Fehlercodes auf lokalisierte, benutzerseitige Meldungen ab
- Transiente Fehler wiederholen: Netzwerk- oder Timeout-Fehler können bei einem erneuten Versuch erfolgreich sein