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

OpenMed uses a unified JSON error envelope and structured exception handlers in 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 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: Raises Exception for model loading failures (missing files, architecture mismatches).
  • openmed/core/config.py: Raises ValueError for invalid configuration values (e.g., DEVICE=CUDA instead of cuda).
  • 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.

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 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) 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:

    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 controls verbosity levels.

  3. Validate the runtime configuration. Instantiate OpenMedConfig to verify environment variables:

    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:

    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:

    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.

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 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 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.

Solution: Enable DEBUG logging to identify the missing library, then reinstall with the correct extras per 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:

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:

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.
  • 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 and processing modules.
  • Validate configurations using OpenMedConfig.from_env() and model names against 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 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.

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. 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.

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, 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 using list_available_models().

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →