2025-12-09 17:51:05 +00:00
---
2026-02-11 10:44:34 -05:00
summary: "Configuration overview: common tasks, quick setup, and links to the full reference"
2025-12-09 17:51:05 +00:00
read_when:
2026-02-11 10:44:34 -05:00
- Setting up OpenClaw for the first time
- Looking for common configuration patterns
- Navigating to specific config sections
2026-01-31 16:04:03 -05:00
title: "Configuration"
2025-12-09 17:51:05 +00:00
---
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
# Configuration
2025-12-03 15:45:32 +00:00
2026-02-11 10:44:34 -05:00
OpenClaw reads an optional < Tooltip tip = "JSON5 supports comments and trailing commas" > **JSON5**</ Tooltip > config from `~/.openclaw/openclaw.json` .
2025-12-03 15:45:32 +00:00
2026-02-11 10:44:34 -05:00
If the file is missing, OpenClaw uses safe defaults. Common reasons to add a config:
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
- Connect channels and control who can message the bot
- Set models, tools, sandboxing, or automation (cron, hooks)
- Tune sessions, media, networking, or UI
2025-12-03 15:45:32 +00:00
2026-02-11 10:44:34 -05:00
See the [full reference ](/gateway/configuration-reference ) for every available field.
2026-01-05 22:22:15 -08:00
2026-02-11 10:44:34 -05:00
< Tip >
**New to configuration?** Start with `openclaw onboard` for interactive setup, or check out the [Configuration Examples ](/gateway/configuration-examples ) guide for complete copy-paste configs.
< / Tip >
2026-01-19 03:38:51 +00:00
2026-02-11 10:44:34 -05:00
## Minimal config
2026-01-31 21:13:13 +09:00
2026-01-09 20:42:16 +00:00
```json5
2026-02-11 10:44:34 -05:00
// ~/.openclaw/openclaw.json
2026-01-09 20:42:16 +00:00
{
2026-02-11 10:44:34 -05:00
agents: { defaults: { workspace: "~/.openclaw/workspace" } },
channels: { whatsapp: { allowFrom: ["+15555550123"] } },
2026-01-09 20:42:16 +00:00
}
```
2026-02-11 10:44:34 -05:00
## Editing config
< Tabs >
< Tab title = "Interactive wizard" >
```bash
openclaw onboard # full setup wizard
openclaw configure # config wizard
```
< / Tab >
< Tab title = "CLI (one-liners)" >
```bash
openclaw config get agents.defaults.workspace
openclaw config set agents.defaults.heartbeat.every "2h"
openclaw config unset tools.web.search.apiKey
```
< / Tab >
< Tab title = "Control UI" >
Open [http://127.0.0.1:18789 ](http://127.0.0.1:18789 ) and use the **Config** tab.
The Control UI renders a form from the config schema, with a **Raw JSON** editor as an escape hatch.
< / Tab >
< Tab title = "Direct edit" >
Edit `~/.openclaw/openclaw.json` directly. The Gateway watches the file and applies changes automatically (see [hot reload ](#config-hot-reload )).
< / Tab >
< / Tabs >
## Strict validation
< Warning >
OpenClaw only accepts configurations that fully match the schema. Unknown keys, malformed types, or invalid values cause the Gateway to **refuse to start** .
< / Warning >
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
When validation fails:
2025-12-25 23:50:52 +01:00
2026-02-11 10:44:34 -05:00
- The Gateway does not boot
- Only diagnostic commands work (`openclaw doctor` , `openclaw logs` , `openclaw health` , `openclaw status` )
- Run `openclaw doctor` to see exact issues
- Run `openclaw doctor --fix` (or `--yes` ) to apply repairs
2026-01-03 21:35:44 +01:00
2026-02-11 10:44:34 -05:00
## Common tasks
2026-01-03 21:35:44 +01:00
2026-02-11 10:44:34 -05:00
< AccordionGroup >
< Accordion title = "Set up a channel (WhatsApp, Telegram, Discord, etc.)" >
Each channel has its own config section under `channels.<provider>` . See the dedicated channel page for setup steps:
2026-01-08 21:49:26 +01:00
2026-02-11 10:44:34 -05:00
- [WhatsApp ](/channels/whatsapp ) — `channels.whatsapp`
- [Telegram ](/channels/telegram ) — `channels.telegram`
- [Discord ](/channels/discord ) — `channels.discord`
- [Slack ](/channels/slack ) — `channels.slack`
- [Signal ](/channels/signal ) — `channels.signal`
- [iMessage ](/channels/imessage ) — `channels.imessage`
- [Google Chat ](/channels/googlechat ) — `channels.googlechat`
- [Mattermost ](/channels/mattermost ) — `channels.mattermost`
- [MS Teams ](/channels/msteams ) — `channels.msteams`
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
All channels share the same DM policy pattern:
2026-01-03 21:35:44 +01:00
2026-02-11 10:44:34 -05:00
```json5
{
channels: {
telegram: {
enabled: true,
botToken: "123:abc",
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["tg:123"], // only for allowlist/open
},
},
}
```
2026-01-07 02:31:51 +01:00
2026-02-11 10:44:34 -05:00
< / Accordion >
2026-01-06 23:22:49 +01:00
2026-02-11 10:44:34 -05:00
< Accordion title = "Choose and configure models" >
Set the primary model and optional fallbacks:
2026-01-19 01:35:17 +00:00
2026-02-11 10:44:34 -05:00
```json5
{
agents: {
defaults: {
model: {
primary: "anthropic/claude-sonnet-4-5",
fallbacks: ["openai/gpt-5.2"],
},
models: {
"anthropic/claude-sonnet-4-5": { alias: "Sonnet" },
"openai/gpt-5.2": { alias: "GPT" },
2026-01-09 12:44:23 +00:00
},
2026-01-31 21:13:13 +09:00
},
},
2026-02-11 10:44:34 -05:00
}
```
2026-01-03 22:11:43 +01:00
2026-02-11 10:44:34 -05:00
- `agents.defaults.models` defines the model catalog and acts as the allowlist for `/model` .
- Model refs use `provider/model` format (e.g. `anthropic/claude-opus-4-6` ).
- See [Models CLI ](/concepts/models ) for switching models in chat and [Model Failover ](/concepts/model-failover ) for auth rotation and fallback behavior.
- For custom/self-hosted providers, see [Custom providers ](/gateway/configuration-reference#custom-providers-and-base-urls ) in the reference.
2026-01-04 14:32:47 +00:00
2026-02-11 10:44:34 -05:00
< / Accordion >
2026-01-07 09:32:49 +00:00
2026-02-11 10:44:34 -05:00
< Accordion title = "Control who can message the bot" >
DM access is controlled per channel via `dmPolicy` :
2026-01-12 10:13:32 -07:00
2026-02-11 10:44:34 -05:00
- `"pairing"` (default): unknown senders get a one-time pairing code to approve
- `"allowlist"` : only senders in `allowFrom` (or the paired allow store)
- `"open"` : allow all inbound DMs (requires `allowFrom: ["*"]` )
- `"disabled"` : ignore all DMs
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
For groups, use `groupPolicy` + `groupAllowFrom` or channel-specific allowlists.
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
See the [full reference ](/gateway/configuration-reference#dm-and-group-access ) for per-channel details.
2026-01-11 01:52:23 +01:00
2026-02-11 10:44:34 -05:00
< / Accordion >
2025-12-23 02:48:57 +01:00
2026-02-11 10:44:34 -05:00
< Accordion title = "Set up group chat mention gating" >
Group messages default to **require mention** . Configure patterns per agent:
2025-12-23 02:48:57 +01:00
2026-02-11 10:44:34 -05:00
```json5
{
agents: {
list: [
2025-12-23 02:48:57 +01:00
{
2026-02-11 10:44:34 -05:00
id: "main",
groupChat: {
mentionPatterns: ["@openclaw ", "openclaw"],
},
2026-01-31 21:13:13 +09:00
},
],
},
2026-02-11 10:44:34 -05:00
channels: {
whatsapp: {
groups: { "*": { requireMention: true } },
},
},
}
```
2026-01-06 11:35:47 -03:00
2026-02-11 10:44:34 -05:00
- **Metadata mentions**: native @-mentions (WhatsApp tap-to-mention, Telegram @bot , etc.)
- **Text patterns**: regex patterns in `mentionPatterns`
- See [full reference ](/gateway/configuration-reference#group-chat-mention-gating ) for per-channel overrides and self-chat mode.
2026-01-06 11:35:47 -03:00
2026-02-11 10:44:34 -05:00
< / Accordion >
2026-01-10 16:32:21 +01:00
2026-02-11 10:44:34 -05:00
< Accordion title = "Configure sessions and resets" >
Sessions control conversation continuity and isolation:
2026-01-06 11:35:47 -03:00
2026-02-11 10:44:34 -05:00
```json5
{
session: {
dmScope: "per-channel-peer", // recommended for multi-user
reset: {
mode: "daily",
atHour: 4,
idleMinutes: 120,
},
},
}
```
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
- `dmScope` : `main` (shared) | `per-peer` | `per-channel-peer` | `per-account-channel-peer`
- See [Session Management ](/concepts/session ) for scoping, identity links, and send policy.
- See [full reference ](/gateway/configuration-reference#session ) for all fields.
2026-01-06 11:35:47 -03:00
2026-02-11 10:44:34 -05:00
< / Accordion >
2026-01-12 06:47:57 +00:00
2026-02-11 10:44:34 -05:00
< Accordion title = "Enable sandboxing" >
Run agent sessions in isolated Docker containers:
2026-01-12 06:47:57 +00:00
2026-02-11 10:44:34 -05:00
```json5
{
agents: {
defaults: {
sandbox: {
mode: "non-main", // off | non-main | all
scope: "agent", // session | agent | shared
2026-01-31 21:13:13 +09:00
},
2026-02-11 10:44:34 -05:00
},
2026-01-31 21:13:13 +09:00
},
2026-02-11 10:44:34 -05:00
}
```
2026-01-17 17:35:40 +00:00
2026-02-11 10:44:34 -05:00
Build the image first: `scripts/sandbox-setup.sh`
2026-01-17 17:35:40 +00:00
2026-02-11 10:44:34 -05:00
See [Sandboxing ](/gateway/sandboxing ) for the full guide and [full reference ](/gateway/configuration-reference#sandbox ) for all options.
2026-01-13 00:22:03 +00:00
2026-02-11 10:44:34 -05:00
< / Accordion >
2026-01-13 00:22:03 +00:00
2026-02-11 10:44:34 -05:00
< Accordion title = "Set up heartbeat (periodic check-ins)" >
```json5
{
agents: {
defaults: {
heartbeat: {
every: "30m",
target: "last",
2026-01-31 21:13:13 +09:00
},
2026-02-11 10:44:34 -05:00
},
2026-01-31 21:13:13 +09:00
},
2026-02-11 10:44:34 -05:00
}
```
2026-01-13 00:22:03 +00:00
2026-02-11 10:44:34 -05:00
- `every` : duration string (`30m` , `2h` ). Set `0m` to disable.
- `target` : `last` | `whatsapp` | `telegram` | `discord` | `none`
- See [Heartbeat ](/gateway/heartbeat ) for the full guide.
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
< / Accordion >
2026-01-13 00:22:03 +00:00
2026-02-11 10:44:34 -05:00
< Accordion title = "Configure cron jobs" >
```json5
{
cron: {
enabled: true,
maxConcurrentRuns: 2,
sessionRetention: "24h",
},
}
```
2025-12-27 00:48:15 +00:00
2026-02-11 10:44:34 -05:00
See [Cron jobs ](/automation/cron-jobs ) for the feature overview and CLI examples.
2025-12-27 00:48:15 +00:00
2026-02-11 10:44:34 -05:00
< / Accordion >
2026-01-09 13:56:00 -03:00
2026-02-11 10:44:34 -05:00
< Accordion title = "Set up webhooks (hooks)" >
Enable HTTP webhook endpoints on the Gateway:
2026-01-09 13:56:00 -03:00
2026-02-11 10:44:34 -05:00
```json5
{
hooks: {
enabled: true,
token: "shared-secret",
path: "/hooks",
mappings: [
2026-01-09 13:56:00 -03:00
{
2026-02-11 10:44:34 -05:00
match: { path: "gmail" },
action: "agent",
agentId: "main",
deliver: true,
2026-01-31 21:13:13 +09:00
},
],
},
2026-02-11 10:44:34 -05:00
}
```
2026-01-09 13:56:00 -03:00
2026-02-11 10:44:34 -05:00
See [full reference ](/gateway/configuration-reference#hooks ) for all mapping options and Gmail integration.
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
< / Accordion >
2026-01-09 13:56:00 -03:00
2026-02-11 10:44:34 -05:00
< Accordion title = "Configure multi-agent routing" >
Run multiple isolated agents with separate workspaces and sessions:
2026-01-12 05:57:49 +00:00
2026-02-11 10:44:34 -05:00
```json5
{
agents: {
list: [
{ id: "home", default: true, workspace: "~/.openclaw/workspace-home" },
{ id: "work", workspace: "~/.openclaw/workspace-work" },
2026-01-31 21:13:13 +09:00
],
},
2026-02-11 10:44:34 -05:00
bindings: [
{ agentId: "home", match: { channel: "whatsapp", accountId: "personal" } },
{ agentId: "work", match: { channel: "whatsapp", accountId: "biz" } },
],
}
```
2026-01-11 12:11:12 +00:00
2026-02-11 10:44:34 -05:00
See [Multi-Agent ](/concepts/multi-agent ) and [full reference ](/gateway/configuration-reference#multi-agent-routing ) for binding rules and per-agent access profiles.
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
< / Accordion >
2026-01-11 12:11:12 +00:00
2026-02-11 10:44:34 -05:00
< Accordion title = "Split config into multiple files ($include)" >
Use `$include` to organize large configs:
2026-01-11 12:11:12 +00:00
2026-02-11 10:44:34 -05:00
```json5
// ~/.openclaw/openclaw.json
{
gateway: { port: 18789 },
agents: { $include: "./agents.json5" },
broadcast: {
$include: ["./clients/a.json5", "./clients/b.json5"],
2026-01-31 21:13:13 +09:00
},
2026-02-11 10:44:34 -05:00
}
```
2025-12-30 04:14:36 +01:00
2026-02-11 10:44:34 -05:00
- **Single file**: replaces the containing object
- **Array of files**: deep-merged in order (later wins)
- **Sibling keys**: merged after includes (override included values)
- **Nested includes**: supported up to 10 levels deep
- **Relative paths**: resolved relative to the including file
- **Error handling**: clear errors for missing files, parse errors, and circular includes
2025-12-20 02:08:04 +00:00
2026-02-11 10:44:34 -05:00
< / Accordion >
< / AccordionGroup >
2026-01-03 23:34:18 +01:00
2026-02-11 10:44:34 -05:00
## Config hot reload
2026-01-24 21:01:42 +00:00
2026-02-11 10:44:34 -05:00
The Gateway watches `~/.openclaw/openclaw.json` and applies changes automatically — no manual restart needed for most settings.
2026-01-03 19:52:24 +00:00
2026-02-11 10:44:34 -05:00
### Reload modes
2026-01-03 19:52:24 +00:00
2026-02-11 10:44:34 -05:00
| Mode | Behavior |
| ---------------------- | --------------------------------------------------------------------------------------- |
| ** `hybrid` ** (default) | Hot-applies safe changes instantly. Automatically restarts for critical ones. |
| ** `hot` ** | Hot-applies safe changes only. Logs a warning when a restart is needed — you handle it. |
| ** `restart` ** | Restarts the Gateway on any config change, safe or not. |
| ** `off` ** | Disables file watching. Changes take effect on the next manual restart. |
2026-01-03 19:52:24 +00:00
```json5
{
gateway: {
2026-02-11 10:44:34 -05:00
reload: { mode: "hybrid", debounceMs: 300 },
2026-01-31 21:13:13 +09:00
},
2025-12-24 14:32:55 +00:00
}
```
2026-02-11 10:44:34 -05:00
### What hot-applies vs what needs a restart
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
Most fields hot-apply without downtime. In `hybrid` mode, restart-required changes are handled automatically.
2025-12-24 14:32:55 +00:00
2026-02-11 10:44:34 -05:00
| Category | Fields | Restart needed? |
| ------------------- | -------------------------------------------------------------------- | --------------- |
| Channels | `channels.*` , `web` (WhatsApp) — all built-in and extension channels | No |
| Agent & models | `agent` , `agents` , `models` , `routing` | No |
| Automation | `hooks` , `cron` , `agent.heartbeat` | No |
| Sessions & messages | `session` , `messages` | No |
| Tools & media | `tools` , `browser` , `skills` , `audio` , `talk` | No |
| UI & misc | `ui` , `logging` , `identity` , `bindings` | No |
| Gateway server | `gateway.*` (port, bind, auth, tailscale, TLS, HTTP) | **Yes** |
| Infrastructure | `discovery` , `canvasHost` , `plugins` | **Yes** |
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
< Note >
`gateway.reload` and `gateway.remote` are exceptions — changing them does **not** trigger a restart.
< / Note >
2025-12-24 14:32:55 +00:00
2026-02-11 10:44:34 -05:00
## Config RPC (programmatic updates)
2025-12-24 14:32:55 +00:00
2026-02-11 10:44:34 -05:00
< AccordionGroup >
< Accordion title = "config.apply (full replace)" >
Validates + writes the full config and restarts the Gateway in one step.
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
< Warning >
`config.apply` replaces the **entire config** . Use `config.patch` for partial updates, or `openclaw config set` for single keys.
< / Warning >
2025-12-24 19:39:36 +00:00
2026-02-11 10:44:34 -05:00
Params:
2025-12-24 19:39:36 +00:00
2026-02-11 10:44:34 -05:00
- `raw` (string) — JSON5 payload for the entire config
- `baseHash` (optional) — config hash from `config.get` (required when config exists)
- `sessionKey` (optional) — session key for the post-restart wake-up ping
- `note` (optional) — note for the restart sentinel
- `restartDelayMs` (optional) — delay before restart (default 2000)
2025-12-24 19:39:36 +00:00
2026-02-11 10:44:34 -05:00
```bash
openclaw gateway call config.get --params '{}' # capture payload.hash
openclaw gateway call config.apply --params '{
"raw": "{ agents: { defaults: { workspace: \"~/.openclaw/workspace\" } } }",
"baseHash": "< hash > ",
"sessionKey": "agent:main:whatsapp:dm:+15555550123"
}'
```
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
< / Accordion >
2026-01-09 19:59:45 +01:00
2026-02-11 10:44:34 -05:00
< Accordion title = "config.patch (partial update)" >
Merges a partial update into the existing config (JSON merge patch semantics):
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
- Objects merge recursively
- `null` deletes a key
- Arrays replace
2026-01-03 03:03:49 +01:00
2026-02-11 10:44:34 -05:00
Params:
2025-12-24 21:56:21 +00:00
2026-02-11 10:44:34 -05:00
- `raw` (string) — JSON5 with just the keys to change
- `baseHash` (required) — config hash from `config.get`
- `sessionKey` , `note` , `restartDelayMs` — same as `config.apply`
2025-12-18 11:36:46 +01:00
2026-02-11 10:44:34 -05:00
```bash
openclaw gateway call config.patch --params '{
"raw": "{ channels: { telegram: { groups: { \"*\": { requireMention: false } } } } }",
"baseHash": "< hash > "
}'
```
2025-12-18 11:36:46 +01:00
2026-02-11 10:44:34 -05:00
< / Accordion >
< / AccordionGroup >
2025-12-18 11:36:46 +01:00
2026-02-11 10:44:34 -05:00
## Environment variables
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
OpenClaw reads env vars from the parent process plus:
2025-12-18 11:36:46 +01:00
2026-02-11 10:44:34 -05:00
- `.env` from the current working directory (if present)
- `~/.openclaw/.env` (global fallback)
2026-01-31 21:13:13 +09:00
2026-02-11 10:44:34 -05:00
Neither file overrides existing env vars. You can also set inline env vars in config:
2026-01-04 15:22:47 +01:00
2025-12-18 11:36:46 +01:00
```json5
{
2026-02-11 10:44:34 -05:00
env: {
OPENROUTER_API_KEY: "sk-or-...",
vars: { GROQ_API_KEY: "gsk-..." },
2026-01-31 21:13:13 +09:00
},
2025-12-18 11:36:46 +01:00
}
```
2026-02-11 10:44:34 -05:00
< Accordion title = "Shell env import (optional)" >
If enabled and expected keys aren't set, OpenClaw runs your login shell and imports only the missing keys:
2026-01-16 05:28:33 +00:00
2025-12-17 17:01:10 +01:00
```json5
{
2026-02-11 10:44:34 -05:00
env: {
shellEnv: { enabled: true, timeoutMs: 15000 },
2026-01-31 21:13:13 +09:00
},
2025-12-17 17:01:10 +01:00
}
```
2026-02-11 10:44:34 -05:00
Env var equivalent: `OPENCLAW_LOAD_SHELL_ENV=1`
< / Accordion >
2026-01-27 00:32:11 +11:00
2026-02-11 10:44:34 -05:00
< Accordion title = "Env var substitution in config values" >
Reference env vars in any config string value with `${VAR_NAME}` :
2026-01-27 00:32:11 +11:00
```json5
{
2026-02-11 10:44:34 -05:00
gateway: { auth: { token: "${OPENCLAW_GATEWAY_TOKEN}" } },
models: { providers: { custom: { apiKey: "${CUSTOM_API_KEY}" } } },
2026-01-27 00:32:11 +11:00
}
```
2026-02-11 10:44:34 -05:00
Rules:
2025-12-17 17:01:10 +01:00
2026-02-11 10:44:34 -05:00
- Only uppercase names matched: `[A-Z_][A-Z0-9_]*`
- Missing/empty vars throw an error at load time
- Escape with `$${VAR}` for literal output
- Works inside `$include` files
- Inline substitution: `"${BASE}/v1"` → `"https://api.example.com/v1"`
2025-12-17 17:01:10 +01:00
2026-02-11 10:44:34 -05:00
< / Accordion >
2025-12-03 15:45:32 +00:00
2026-02-11 10:44:34 -05:00
See [Environment ](/help/environment ) for full precedence and sources.
2025-12-03 15:45:32 +00:00
2026-02-11 10:44:34 -05:00
## Full reference
fix: unify session maintenance and cron run pruning (#13083)
* fix: prune stale session entries, cap entry count, and rotate sessions.json
The sessions.json file grows unbounded over time. Every heartbeat tick (default: 30m)
triggers multiple full rewrites, and session keys from groups, threads, and DMs
accumulate indefinitely with large embedded objects (skillsSnapshot,
systemPromptReport). At >50MB the synchronous JSON parse blocks the event loop,
causing Telegram webhook timeouts and effectively taking the bot down.
Three mitigations, all running inside saveSessionStoreUnlocked() on every write:
1. Prune stale entries: remove entries with updatedAt older than 30 days
(configurable via session.maintenance.pruneDays in openclaw.json)
2. Cap entry count: keep only the 500 most recently updated entries
(configurable via session.maintenance.maxEntries). Entries without updatedAt
are evicted first.
3. File rotation: if the existing sessions.json exceeds 10MB before a write,
rename it to sessions.json.bak.{timestamp} and keep only the 3 most recent
backups (configurable via session.maintenance.rotateBytes).
All three thresholds are configurable under session.maintenance in openclaw.json
with Zod validation. No env vars.
Existing tests updated to use Date.now() instead of epoch-relative timestamps
(1, 2, 3) that would be incorrectly pruned as stale.
27 new tests covering pruning, capping, rotation, and integration scenarios.
* feat: auto-prune expired cron run sessions (#12289)
Add TTL-based reaper for isolated cron run sessions that accumulate
indefinitely in sessions.json.
New config option:
cron.sessionRetention: string | false (default: '24h')
The reaper runs piggy-backed on the cron timer tick, self-throttled
to sweep at most every 5 minutes. It removes session entries matching
the pattern cron:<jobId>:run:<uuid> whose updatedAt + retention < now.
Design follows the Kubernetes ttlSecondsAfterFinished pattern:
- Sessions are persisted normally (observability/debugging)
- A periodic reaper prunes expired entries
- Configurable retention with sensible default
- Set to false to disable pruning entirely
Files changed:
- src/config/types.cron.ts: Add sessionRetention to CronConfig
- src/config/zod-schema.ts: Add Zod validation for sessionRetention
- src/cron/session-reaper.ts: New reaper module (sweepCronRunSessions)
- src/cron/session-reaper.test.ts: 12 tests covering all paths
- src/cron/service/state.ts: Add cronConfig/sessionStorePath to deps
- src/cron/service/timer.ts: Wire reaper into onTimer tick
- src/gateway/server-cron.ts: Pass config and session store path to deps
Closes #12289
* fix: sweep cron session stores per agent
* docs: add changelog for session maintenance (#13083) (thanks @skyfallsin, @Glucksberg)
* fix: add warn-only session maintenance mode
* fix: warn-only maintenance defaults to active session
* fix: deliver maintenance warnings to active session
* docs: add session maintenance examples
* fix: accept duration and size maintenance thresholds
* refactor: share cron run session key check
* fix: format issues and replace defaultRuntime.warn with console.warn
---------
Co-authored-by: Pradeep Elankumaran <pradeepe@gmail.com>
Co-authored-by: Glucksberg <markuscontasul@gmail.com>
Co-authored-by: max <40643627+quotentiroler@users.noreply.github.com>
Co-authored-by: quotentiroler <max.nussbaumer@maxhealth.tech>
2026-02-09 23:42:35 -05:00
2026-02-11 10:44:34 -05:00
For the complete field-by-field reference, see ** [Configuration Reference ](/gateway/configuration-reference )**.
fix: unify session maintenance and cron run pruning (#13083)
* fix: prune stale session entries, cap entry count, and rotate sessions.json
The sessions.json file grows unbounded over time. Every heartbeat tick (default: 30m)
triggers multiple full rewrites, and session keys from groups, threads, and DMs
accumulate indefinitely with large embedded objects (skillsSnapshot,
systemPromptReport). At >50MB the synchronous JSON parse blocks the event loop,
causing Telegram webhook timeouts and effectively taking the bot down.
Three mitigations, all running inside saveSessionStoreUnlocked() on every write:
1. Prune stale entries: remove entries with updatedAt older than 30 days
(configurable via session.maintenance.pruneDays in openclaw.json)
2. Cap entry count: keep only the 500 most recently updated entries
(configurable via session.maintenance.maxEntries). Entries without updatedAt
are evicted first.
3. File rotation: if the existing sessions.json exceeds 10MB before a write,
rename it to sessions.json.bak.{timestamp} and keep only the 3 most recent
backups (configurable via session.maintenance.rotateBytes).
All three thresholds are configurable under session.maintenance in openclaw.json
with Zod validation. No env vars.
Existing tests updated to use Date.now() instead of epoch-relative timestamps
(1, 2, 3) that would be incorrectly pruned as stale.
27 new tests covering pruning, capping, rotation, and integration scenarios.
* feat: auto-prune expired cron run sessions (#12289)
Add TTL-based reaper for isolated cron run sessions that accumulate
indefinitely in sessions.json.
New config option:
cron.sessionRetention: string | false (default: '24h')
The reaper runs piggy-backed on the cron timer tick, self-throttled
to sweep at most every 5 minutes. It removes session entries matching
the pattern cron:<jobId>:run:<uuid> whose updatedAt + retention < now.
Design follows the Kubernetes ttlSecondsAfterFinished pattern:
- Sessions are persisted normally (observability/debugging)
- A periodic reaper prunes expired entries
- Configurable retention with sensible default
- Set to false to disable pruning entirely
Files changed:
- src/config/types.cron.ts: Add sessionRetention to CronConfig
- src/config/zod-schema.ts: Add Zod validation for sessionRetention
- src/cron/session-reaper.ts: New reaper module (sweepCronRunSessions)
- src/cron/session-reaper.test.ts: 12 tests covering all paths
- src/cron/service/state.ts: Add cronConfig/sessionStorePath to deps
- src/cron/service/timer.ts: Wire reaper into onTimer tick
- src/gateway/server-cron.ts: Pass config and session store path to deps
Closes #12289
* fix: sweep cron session stores per agent
* docs: add changelog for session maintenance (#13083) (thanks @skyfallsin, @Glucksberg)
* fix: add warn-only session maintenance mode
* fix: warn-only maintenance defaults to active session
* fix: deliver maintenance warnings to active session
* docs: add session maintenance examples
* fix: accept duration and size maintenance thresholds
* refactor: share cron run session key check
* fix: format issues and replace defaultRuntime.warn with console.warn
---------
Co-authored-by: Pradeep Elankumaran <pradeepe@gmail.com>
Co-authored-by: Glucksberg <markuscontasul@gmail.com>
Co-authored-by: max <40643627+quotentiroler@users.noreply.github.com>
Co-authored-by: quotentiroler <max.nussbaumer@maxhealth.tech>
2026-02-09 23:42:35 -05:00
2025-12-03 15:45:32 +00:00
---
2026-02-11 10:44:34 -05:00
_Related: [Configuration Examples ](/gateway/configuration-examples ) · [Configuration Reference ](/gateway/configuration-reference ) · [Doctor ](/gateway/doctor )_