# How to Troubleshoot OpenMed Errors: A Complete Guide to Debugging the REST API and Core Library

> Troubleshoot OpenMed errors efficiently. Debug REST API and core library issues using structured exceptions, JSON error envelopes, and log inspection. Fix validation, timeout, and runtime failures.

- Repository: [Maziyar Panahi/openmed](https://github.com/maziyarpanahi/openmed)
- Tags: how-to-guide
- Published: 2026-06-13

---

**OpenMed uses a unified JSON error envelope and structured exception handlers in [`openmed/service/app.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/service/app.py) to surface validation, timeout, and runtime failures, while the core library propagates standard Python exceptions that you can debug by setting `OPENMED_LOG_LEVEL=DEBUG` and inspecting server logs.**

OpenMed, developed by `maziyarpanahi/openmed`, separates concerns between a FastAPI REST service and a core NLP library. When you need to troubleshoot OpenMed errors, understanding this architecture is crucial because error handling differs between the HTTP layer—which returns structured JSON envelopes—and the Python API—which raises native exceptions.

## Understanding OpenMed's Error Architecture

### The REST Service Layer

The FastAPI service in [`openmed/service/app.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/service/app.py) implements custom exception handlers that catch specific failure types and return a stable JSON envelope shaped as `{ "error": { "code": "...", "message": "...", "details": {...} } }`.

The handlers include:

- **RequestValidationError**: Triggered by FastAPI/Pydantic input validation failures (HTTP 422). Returns detailed field-level error information.
- **ServiceTimeoutError**: Triggered when inference exceeds `OpenMedConfig.timeout` (HTTP 504). Includes the timeout duration in the details.
- **ValueError**: Catches invalid arguments from the core library (HTTP 400). Surfaces configuration mistakes like unsupported device names.
- **StarletteHTTPException**: Handles explicit HTTP errors such as 404 Not Found.
- **Exception (fallback)**: Catches unhandled exceptions (HTTP 500) and returns a generic "Internal server error" message while logging the full traceback server-side.

All handlers funnel through the `_error_response()` helper function to ensure consistent formatting.

### The Core Library Layer

The core library rarely uses custom exception types. Instead, it propagates standard Python exceptions:

- **[`openmed/core/models.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/core/models.py)**: Raises `Exception` for model loading failures (missing files, architecture mismatches).
- **[`openmed/core/config.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/core/config.py)**: Raises `ValueError` for invalid configuration values (e.g., `DEVICE=CUDA` instead of `cuda`).
- **[`openmed/processing/batch.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/processing/batch.py)**: Wraps batch processing errors as generic `Exception`.
- **`openmed/ner/*`**: Propagates `Exception` for missing label maps.
- **`openmed/mlx/*`**: Raises `Exception` for MLX backend issues (missing native libraries).

Because the core bubbles up native exceptions, the REST service catches these and maps them to HTTP status codes.

## Interpreting HTTP Error Codes and Responses

When troubleshooting the REST API, look for these specific patterns:

**422 Validation Error**

Occurs when the JSON payload fails Pydantic validation. Check the `details` array for specific field errors. Verify your request against the `AnalyzeRequest` or `PIIExtractRequest` schemas exposed in [`openmed/service/app.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/service/app.py).

**504 Timeout**

Indicates the request exceeded the `OpenMedConfig.timeout` value. The response includes `timeout_seconds` in the error details. Increase the timeout via `export OPENMED_TIMEOUT=30` or optimize your model selection.

**400 Bad Request**

Typically surfaces as a `ValueError` from the core. Common causes include requesting a model name not found in [`openmed/core/model_registry.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/core/model_registry.py) or misconfiguring the device parameter.

**500 Internal Server Error**

The fallback handler for unhandled exceptions. This indicates an unexpected bug in model loading ([`openmed/core/models.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/core/models.py)) or backend libraries (`torch`, `transformers`, `mlx`). Enable debug logging to capture the full traceback.

## Step-by-Step Debugging Workflow

Follow this systematic approach to isolate and resolve failures:

1. **Inspect the HTTP response body**. The JSON envelope contains the error code and message. Parse the `error.details` object for specific field validation failures or timeout durations.

2. **Enable detailed logging**. Set the environment variable before starting the service:
   ```bash
   export OPENMED_LOG_LEVEL=DEBUG
   uv run uvicorn openmed.service.app:app --host 0.0.0.0 --port 8000
   ```

   The logger configuration in [`openmed/utils/logging.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/utils/logging.py) controls verbosity levels.

3. **Validate the runtime configuration**. Instantiate `OpenMedConfig` to verify environment variables:
   ```python
   from openmed.core.config import OpenMedConfig
   
   cfg = OpenMedConfig.from_env()
   print(cfg)  # Verify device, cache_dir, timeout values

   ```

   Mis-typed values (e.g., `DEVICE=GPU` instead of `cuda`) raise `ValueError` before model loading begins.

4. **Confirm model availability**. Check the model registry to ensure the requested model exists:
   ```python
   from openmed.core.model_registry import ModelRegistry
   
   registry = ModelRegistry()
   print(registry.list_available_models())
   ```

   Alternatively, use `runtime.loaded_models()` to see which models are currently in memory.

5. **Reproduce locally with the Python API**. Bypass the REST layer to see raw stack traces:
   ```python
   from openmed import analyze_text
   
   try:
       analyze_text("Patient has hypertension.", model_name="nonexistent_model")
   except Exception:
       import traceback
       traceback.print_exc()
   ```

6. **Clear the service runtime**. If suspecting a stale state, clear the cache directory (`~/.cache/openmed`) or restart the process to force `ServiceRuntime` reinitialization via `_get_service_runtime()` in [`openmed/service/app.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/service/app.py).

## Common Error Patterns and Solutions

**Symptom: 422 Validation Error with field-level details**

Root cause: Missing required fields or type mismatches in the JSON payload.

Solution: Validate your request against the OpenAPI schema at [`/openapi.json`](https://github.com/maziyarpanahi/openmed/blob/main//openapi.json) or the Pydantic models (`AnalyzeRequest`, `PIIExtractRequest`) defined in the service layer.

**Symptom: 504 Timeout with "Request exceeded configured timeout"**

Root cause: Model inference exceeds the default `OpenMedConfig.timeout`.

Solution: Increase the timeout environment variable (`export OPENMED_TIMEOUT=60`) or switch to a lighter model backbone.

**Symptom: 400 Bad Request with "Model ... not found"**

Root cause: The model name is not registered in [`openmed/core/model_registry.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/core/model_registry.py) or not pre-loaded.

Solution: Verify the model name against the registry or preload models via `runtime.preload_models()` before making requests.

**Symptom: 500 Internal Server Error with "Internal server error"**

Root cause: Missing dependencies (`torch`, `transformers`, `mlx`) or corrupt model files in [`openmed/core/models.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/core/models.py).

Solution: Enable `DEBUG` logging to identify the missing library, then reinstall with the correct extras per [`docs/getting-started.md`](https://github.com/maziyarpanahi/openmed/blob/main/docs/getting-started.md).

**Symptom: Empty response or hanging request**

Root cause: Silent process death, often a segmentation fault in native MLX backend code.

Solution: Ensure platform compatibility (Apple Silicon for MLX, Linux for Torch) and check OS-level logs for segfaults.

## Debugging with the Python API vs. REST Endpoint

When using the Python API directly, exceptions propagate without the JSON envelope, providing immediate stack traces:

```python
from openmed import analyze_text
from openmed.core.config import OpenMedConfig
from openmed.core.model_loader import ModelLoader

cfg = OpenMedConfig.from_env()
loader = ModelLoader(config=cfg)

try:
    result = analyze_text(
        "Patient has hypertension.",
        model_name="disease_detection_superclinical",
        config=cfg,
        loader=loader,
        output_format="dict",
    )
except Exception:
    import traceback
    traceback.print_exc()

```

When consuming the REST API, always check for the error envelope before processing the response:

```python
import requests

resp = requests.post(
    "http://localhost:8000/analyze",
    json={"text": "test", "model_name": "unknown"},
)

payload = resp.json()
if "error" in payload:
    print(f"Error code: {payload['error']['code']}")
    print(f"Message: {payload['error']['message']}")
    if payload["error"].get("details"):
        print(f"Details: {payload['error']['details']}")
else:
    print("Success:", payload)

```

## Summary

- OpenMed errors follow a **unified JSON envelope** structure (`{ "error": { "code", "message", "details" } }`) generated by handlers in [`openmed/service/app.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/service/app.py).
- The REST service maps specific exceptions to HTTP status codes: **422** for validation, **504** for timeouts, **400** for value errors, and **500** for unhandled exceptions.
- The core library raises **standard Python exceptions** (`ValueError`, `Exception`) rather than custom types, making server logs essential for diagnosing 500 errors.
- Enable **debug logging** by setting `OPENMED_LOG_LEVEL=DEBUG` to capture full stack traces from [`openmed/core/models.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/core/models.py) and processing modules.
- Validate configurations using `OpenMedConfig.from_env()` and model names against [`openmed/core/model_registry.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/core/model_registry.py) before runtime.
- For complex failures, **reproduce the issue locally** using the Python API to bypass the HTTP abstraction and see raw tracebacks.

## Frequently Asked Questions

### How do I enable debug logging in OpenMed?

Set the `OPENMED_LOG_LEVEL` environment variable to `DEBUG` before starting the server. The logging configuration in [`openmed/utils/logging.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/utils/logging.py) reads this variable and increases verbosity, writing detailed stack traces to stdout. Use `export OPENMED_LOG_LEVEL=DEBUG` in bash or set it in your Docker environment.

### What does a 422 validation error mean in OpenMed?

A 422 error indicates that your JSON payload failed FastAPI/Pydantic validation. This typically occurs when required fields are missing or data types are incorrect (e.g., passing a string where an integer is expected). Check the `error.details` array in the response for specific field-level messages, and validate your request against the `AnalyzeRequest` schema in [`openmed/service/app.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/service/app.py).

### Why am I getting a 500 internal error when loading models?

HTTP 500 errors during model loading usually stem from missing dependencies or corrupt model files in [`openmed/core/models.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/core/models.py). The core library raises a standard `Exception` that the service catches and returns as a 500. Enable `DEBUG` logging to see the underlying traceback, then verify that you installed the required extras (e.g., `pip install openmed[mlx]` or `openmed[torch]`) as specified in [`docs/getting-started.md`](https://github.com/maziyarpanahi/openmed/blob/main/docs/getting-started.md).

### How can I check which models are loaded in OpenMed?

You can inspect the currently loaded models by accessing the `ServiceRuntime` via the `_get_service_runtime()` function in [`openmed/service/app.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/service/app.py), or by calling `runtime.loaded_models()` if you have a runtime instance. For a list of available models (not necessarily loaded), query the `ModelRegistry` class in [`openmed/core/model_registry.py`](https://github.com/maziyarpanahi/openmed/blob/main/openmed/core/model_registry.py) using `list_available_models()`.