# Nanobot Config Schema Versioning: How HKUDS/nanobot Manages Configuration Evolution

> Learn how Nanobot config schema versioning automatically migrates fields and validates deprecated names using Pydantic. Discover hassle-free configuration evolution with HKUDS/nanobot.

- Repository: [✨Data Intelligence Lab@HKU✨/nanobot](https://github.com/HKUDS/nanobot)
- Tags: internals
- Published: 2026-07-22

---

**Nanobot config schema versioning relies on an implicit, schema-driven approach where the Pydantic-based `Config` model automatically migrates missing fields via defaults and validates deprecated names without requiring an explicit version field.**

The HKUDS/nanobot project stores all runtime settings in a centralized configuration system that eliminates traditional version numbers. Instead of maintaining a `schema_version` field, Nanobot config schema versioning uses the Python type definitions themselves as the source of truth. When the application starts, it parses the user's `~/.nanobot/config.json` against the `Config` model defined in [`nanobot/config/schema.py`](https://github.com/HKUDS/nanobot/blob/main/nanobot/config/schema.py), allowing the structure to evolve while preserving backward compatibility.

## Understanding Nanobot Config Schema Versioning

### The Root Configuration Model

All configuration logic originates from the `Config` class in [`nanobot/config/schema.py`](https://github.com/HKUDS/nanobot/blob/main/nanobot/config/schema.py). This root Pydantic model aggregates specialized sections—`AgentsConfig`, `ChannelsConfig`, `ProvidersConfig`, `ToolsConfig`, and others—each implemented as typed classes with explicit defaults. The `Config` class also applies environment overrides using the prefix `NANOBOT_`, ensuring that environment variables take precedence over file-based settings.

### Implicit Version Management

Rather than storing a version identifier in JSON, Nanobot determines compatibility by overlaying user configurations atop default values defined in code. When `load_config()` from [`nanobot/config/loader.py`](https://github.com/HKUDS/nanobot/blob/main/nanobot/config/loader.py) executes, it resolves the file path via [`nanobot/config/paths.py`](https://github.com/HKUDS/nanobot/blob/main/nanobot/config/paths.py) and parses the JSON content. Pydantic automatically populates any missing fields from the class definitions, effectively performing a zero-downtime migration whenever new releases add parameters.

## Runtime Configuration Resolution

### Loading and Persisting Settings

The configuration lifecycle begins with the `load_config()` function, which returns a fully instantiated `Config` object. Runtime modifications persist through the `save()` method, which writes updates back to the user's config file.

```python
from nanobot.config.loader import load_config

# Load defaults + user file + environment overrides

cfg = load_config()  # Returns Config instance

# Update provider credentials and persist

cfg.providers.openai.api_key = "sk-****"
cfg.save()  # Writes to ~/.nanobot/config.json

```

### Model Preset Resolution

Model-specific settings reside in `ModelPresetConfig` instances. The `Config.resolve_preset()` method determines the active preset based on `agents.defaults.model_preset`, falling back to the implicit default preset defined in `Config.resolve_default_preset`. This resolution mechanism allows new preset fields to be added to the schema without breaking existing installations.

```python

# Access the active model preset (handles versioned defaults automatically)

preset = cfg.resolve_preset()  # Returns ModelPresetConfig

# Change preset at runtime

cfg.agents.defaults.model_preset = "my-high-quality"
new_preset = cfg.resolve_preset()  # Returns updated configuration

```

### Dynamic Provider Matching

The `Config._match_provider` method contains the logic that maps model names to provider configurations. When calling `cfg.get_provider(model="gpt-4o-mini")`, the system respects newly added providers while maintaining compatibility with older model strings. This centralization ensures the schema can grow to support new LLM providers without requiring users to manually update their configuration structure.

## Migration and Validation Mechanisms

### Automatic Field Migration

When new releases introduce fields—for example, a new provider in `ProvidersConfig` or additional flags in `ToolsConfig`—the corresponding Pydantic classes receive default values. Existing user configurations remain compatible because the loader overlays the user file on top of these code-defined defaults. This schema-driven approach ensures that missing fields are automatically populated according to the current codebase's expectations.

### Deprecated Name Validation

The validator `Config._validate_model_preset` actively scans for reserved or deprecated identifiers, such as the preset name "default". When the loader detects an incompatibility, it raises a descriptive `ValueError` that guides the user to update their configuration to match the current schema requirements, preventing silent failures from outdated settings.

## Summary

- **Nanobot config schema versioning** eliminates explicit version fields by using Pydantic model definitions as the single source of truth.
- The `Config` class in [`nanobot/config/schema.py`](https://github.com/HKUDS/nanobot/blob/main/nanobot/config/schema.py) aggregates all sections, with `load_config()` in [`nanobot/config/loader.py`](https://github.com/HKUDS/nanobot/blob/main/nanobot/config/loader.py) handling the merge of user settings, defaults, and environment variables prefixed with `NANOBOT_`.
- **Automatic migration** occurs when new fields are added, populated from class defaults in `AgentsConfig`, `ProvidersConfig`, and other section models.
- Runtime preset resolution via `resolve_preset()` and provider matching through `_match_provider` allow the schema to evolve without user intervention.
- Deprecated configurations trigger clear validation errors through `_validate_model_preset`, ensuring users receive actionable feedback to update their `~/.nanobot/config.json`.

## Frequently Asked Questions

### Does Nanobot store a version number in the config file?

No. The HKUDS/nanobot repository does not include an explicit schema version field in `~/.nanobot/config.json`. Instead, versioning is implicit: the Pydantic model definitions in [`nanobot/config/schema.py`](https://github.com/HKUDS/nanobot/blob/main/nanobot/config/schema.py) act as the schema version, with default values ensuring backward compatibility when new fields are introduced.

### How does Nanobot handle missing configuration fields after an update?

When a new release adds fields to `Config` or its sub-models like `ToolsConfig`, the `load_config()` function automatically applies the defaults defined in the Pydantic classes. This schema-driven approach means existing configurations seamlessly inherit new settings without requiring manual migration scripts.

### What happens if I use a deprecated preset name?

The `Config._validate_model_preset` validator checks for reserved names such as "default". If your configuration contains deprecated identifiers, Nanobot raises a descriptive `ValueError` during startup, directing you to update the `agents.defaults.model_preset` value to comply with the current schema.

### Can I change the active model preset at runtime?

Yes. You can modify `cfg.agents.defaults.model_preset` on the loaded `Config` instance and call `cfg.resolve_preset()` to retrieve the updated `ModelPresetConfig`. Changes persist when you call `cfg.save()`, which writes the updated configuration back to the JSON file.