# Handle errors

Location: [gRPC API](https://developer.m-files.com/APIs/gRPC-API/index.md) > [Guides](https://developer.m-files.com/APIs/gRPC-API/index.md#guides)

HTML version: https://developer.m-files.com/APIs/gRPC-API/guides/errors/

A failed call ends with a gRPC status. For M-Files errors the status is `UNKNOWN`, and the
M-Files error code (an HRESULT) and the error stack travel in trailers. Decode them, act on the
codes you handle, and report the rest.

## How errors arrive

| What | Where |
|---|---|
| gRPC status | `UNKNOWN` for M-Files errors; `UNAUTHENTICATED` for a missing or expired session (no trailers); `UNAVAILABLE` and others from the transport |
| Message | The status message: the message of the top-level error |
| Error stack | Trailer `mfiles-error-serialized-bin`: a serialized [RPCErrorInfoArray](https://developer.m-files.com/APIs/gRPC-API/Reference/Messages/MFiles.RPCErrorInfoArray/index.md), ordered from the original cause (first) to the top-level error (last) |
| Diagnostic text | Trailer `mfiles-error-text-bin`: the stack as UTF-8 text, for logs |

`error_code` is a signed 32-bit integer; mask it with `0xFFFFFFFF` to compare it with codes
written in hex. Details and the table of common codes: [Errors](https://developer.m-files.com/APIs/gRPC-API/runtime/errors/index.md).

## What the helpers give you

The samples' helpers turn a failed call into an error object with the same parts in every
language:

| Part | Python (`MFilesError`) | C# (`MFilesException`) | Go (`*mfilesclient.Error`) |
|---|---|---|---|
| Error code | `hresult`, `hresult_hex`, `hresult_signed` | `ErrorCode`, `ErrorCodeHex` | `HResult`, `SignedHResult()` |
| All codes, cause first | `codes` | `ErrorCodes` | `Codes` |
| Code anywhere in the stack | `has(code)` | `Has(code)` | `Has(code)` |
| Message | `message` | `Message` | `Message` |
| gRPC status | `status` | `StatusCode` | `Status` |

**The error code** is the code of the last entry of the stack, the top-level error that the
status message describes. If that entry has the generic code `0x80040001`, which only passes an
error on, the code is that of the last entry that has another code. Without an error stack (for
example `UNAUTHENTICATED`) there is no error code.

**Test with "has".** The code you handle can be anywhere in the stack, so test with `has`
rather than comparing the error code. Decoding, in Python:

```python
self.long_message: str = trailers.get(ERROR_TEXT_KEY, b"").decode("utf-8", "replace")
self.stack = list(structs_pb2.RPCErrorInfoArray.FromString(
    trailers.get(ERROR_STACK_KEY, b"")).value)
self.codes = [entry.error_code & 0xFFFFFFFF for entry in self.stack]  # cause first
# The top-level error, skipping entries that only pass the error on.
self.hresult: Optional[int] = next(
    (code for code in reversed(self.codes) if code != E_PASS_THROUGH),
    self.codes[-1] if self.codes else None)
```

*Source: [python/mfiles_client.py](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/python/mfiles_client.py)*

## Code

The recipe asks for an object that does not exist, then calls a method that needs vault
administrator rights.

### Python

```python
# Not found: a document ID that does not exist.
objects = client.stub(IRPCObjectOperations_pb2_grpc.IRPCObjectOperationsStub)
try:
    objects.GetObjectInfoAndProperties(
        IRPCObjectOperations_pb2.GetObjectInfoAndPropertiesRequest(
            obj_id=obj_id(0, 999_999_999), latest_version=True))
    raise RuntimeError("Expected the call to fail.")
except MFilesError as error:
    if not error.has(E_NOT_FOUND):
        raise
    print_error("Not found", error)

# Access denied: an administrative call by a user who is not a vault administrator.
admin = client.stub(IRPCPropertyDefsAdmin_pb2_grpc.IRPCPropertyDefsAdminStub)
try:
    admin.GetPropertyDefsAdmin(IRPCPropertyDefsAdmin_pb2.GetPropertyDefsAdminRequest())
    print("Access denied: not provoked, the user is a vault administrator")
except MFilesError as error:
    if not error.has(E_ACCESS_DENIED):
        raise
    print_error("Access denied", error)
```

*Source: [python/recipes/errors.py](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/python/recipes/errors.py)*

### C#

```csharp
// Not found: a document ID that does not exist.
var objects = new IRPCObjectOperations.IRPCObjectOperationsClient(connection.CallInvoker);
try
{
    await objects.GetObjectInfoAndPropertiesAsync(new GetObjectInfoAndPropertiesRequest
    {
        ObjId = ObjId(0, 999_999_999),
        LatestVersion = true,
    });
    throw new InvalidOperationException("Expected the call to fail.");
}
catch (MFilesException e) when (e.Has(NotFound))
{
    Print("Not found", e);
}

// Access denied: an administrative call by a user who is not a vault administrator.
var admin = new IRPCPropertyDefsAdmin.IRPCPropertyDefsAdminClient(connection.CallInvoker);
try
{
    await admin.GetPropertyDefsAdminAsync(new GetPropertyDefsAdminRequest());
    Console.WriteLine("Access denied: not provoked, the user is a vault administrator");
}
catch (MFilesException e) when (e.Has(AccessDenied))
{
    Print("Access denied", e);
}
```

*Source: [csharp/Recipes/Errors.cs](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/csharp/Recipes/Errors.cs)*

### Go

```go
// Not found: a document ID that does not exist.
objects := mfiles.NewIRPCObjectOperationsClient(client.Conn)
_, err = objects.GetObjectInfoAndProperties(ctx, &mfiles.GetObjectInfoAndPropertiesRequest{
	ObjId:         mfilesclient.ObjID(0, 999_999_999),
	LatestVersion: true,
})
if mfilesErr, ok := mfilesclient.AsError(err); ok && mfilesErr.Has(mfilesclient.CodeNotFound) {
	printError("Not found", mfilesErr)
} else {
	return fmt.Errorf("expected a not-found error, got %v", err)
}

// Access denied: an administrative call by a user who is not a vault administrator.
admin := mfiles.NewIRPCPropertyDefsAdminClient(client.Conn)
_, err = admin.GetPropertyDefsAdmin(ctx, &mfiles.GetPropertyDefsAdminRequest{})
switch mfilesErr, ok := mfilesclient.AsError(err); {
case err == nil:
	fmt.Println("Access denied: not provoked, the user is a vault administrator")
case ok && mfilesErr.Has(mfilesclient.CodeAccessDenied):
	printError("Access denied", mfilesErr)
default:
	return err
}
```

*Source: [go/recipes/errors/main.go](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/go/recipes/errors/main.go)*

Output for a normal user (the stack lists each code once):

```text
Not found: gRPC status UNKNOWN, M-Files error 0x8004000B (-2147221493)
  Message: Not found.
  Error stack, original error first: 0x8004000B
Access denied: gRPC status UNKNOWN, M-Files error 0x800407E4 (-2147219484)
  Message: Access denied. You do not have the access rights required by this operation.
  Error stack, original error first: 0x800400A9, 0x800407E4
```

The full access-denied stack is `0x800400A9` followed by six entries of `0x800407E4`; the not-found
stack is eight entries of `0x8004000B`.

## Pitfalls

* **"Not found" also means "not visible".** An object the user has no right to see fails with
  `0x8004000B`, exactly like one that does not exist.
* **The first and last entries can differ.** For access denied, the error code is
  `0x800407E4` and the first entry says what was missing: `0x800400A9` (a vault-level right) or
  `0x800400AA` (rights to the object). Test with `has`, not only with the error code.
* **`UNAUTHENTICATED` has no M-Files code** and no error stack. Log in again and retry; see
  [Authentication and sessions](https://developer.m-files.com/APIs/gRPC-API/runtime/authentication/index.md).
* **Messages contain `\r\n`** and can change between versions: match codes, not text.
* The diagnostic text and the `file` and `line` of each entry name server source files; log them,
  do not show them to end users.

## See also

* [Errors](https://developer.m-files.com/APIs/gRPC-API/runtime/errors/index.md): the full error model and the common error codes.
* [Permissions](https://developer.m-files.com/APIs/gRPC-API/concepts/permissions/index.md): what access denied looks like for objects.
