Skip to content

Models

netclaw assigns models to three roles in the Models section of ~/.netclaw/config/netclaw.json: Main, Fallback, and Compaction. Only Main is required. Fallback and Compaction route to Main when unset. You need at least one provider configured first, and netclaw model edits all of this interactively.

netclaw needs two things from a model: reliable tool calling and enough context for a large first turn. A fresh session with the default tools sent about 10,000 input tokens on its first message in our test runs, before you’ve typed anything of substance. The number moves with your enabled tools, skills, and memory, but plan for a context window of 32k at the least. The 4,096-token floor netclaw enforces is a sanity check, not a working minimum.

RoleWhat it doesA sensible choice
MainEvery conversation turn and tool callThe best tool-calling model you can afford
FallbackTakes over when Main fails (see Failover)A different provider than Main, so one outage doesn’t take out both
CompactionSummarizes context, titles sessions, extracts memoriesA smaller, cheaper model. It never needs to be your best one

A small setup that works: one capable model as Main, and nothing else. Add Compaction when you want to keep a big model free for conversations, and Fallback when you can’t tolerate downtime.

Roles that name the same provider and model share one definition, so an override set for one applies to both.

Tested with Qwen3.8 27B at Q4 on llama.cpp (131,072-token context): tool calls and image inspection worked end to end.

{
"Models": {
"Definitions": {
"qwen-main": {
"Provider": "local-ollama",
"ModelId": "qwen3:30b"
}
},
"Roles": {
"Main": "qwen-main"
}
}
}

Provider must match a key under Providers in the same file. The context window is detected from the provider.

Run netclaw model to open the Model Manager.

Model Manager listing Main on lab-llama with demo-model, and Fallback and Compaction on my-ollama with qwen2:0.5b, with Max out and Thinking at their defaults and all marked Manual

Enter assigns a model to the highlighted role, D discovers models from a provider, P edits the output limit and thinking level, and C clears the highlighted Fallback or Compaction. The Status column reads Manual for a model you set by hand. The role table also has Max out and Thinking columns, which show default until you set them.

Models has two halves. Definitions describe models, and Roles name which definition fills each job:

{
"Models": {
"Definitions": {
"qwen-main": {
"Provider": "remote-gpu",
"ModelId": "qwen3:30b",
"ContextWindow": 32768
},
"qwen-small": {
"Provider": "remote-gpu",
"ModelId": "qwen3:8b",
"ContextWindow": 32768
}
},
"Roles": {
"Main": "qwen-main",
"Fallback": "qwen-small",
"Compaction": "qwen-small"
}
}
}

Point Main at a different definition and the old one keeps its overrides, so switching back restores exactly what you had. Fallback and Compaction share qwen-small here, and editing it moves both.

Definition names are yours. netclaw model set generates <provider>-<model-id> (with punctuation in the model ID turned into hyphens), but nothing depends on that shape. Names match case-insensitively and must be unique. Roles takes exactly Main, Fallback, and Compaction.

FieldTypeDefaultDescription
Providerstring"local-ollama"Key into the Providers dictionary
ModelIdstring"qwen3:30b"Model identifier as the provider API names it
ContextWindowint?detectedContext window in tokens. Wins over the provider-reported value
Provenanceenum?nullWritten by the CLI: Live, Defaults, or Manual. Don’t set it by hand
InputModalitiesstring?detectedComma-separated flags: Text, Image, Audio, Video. Example: "Text, Image"
OutputModalitiesstring?detectedSame values. Example: "Text"
MaxOutputTokensint?32000, clampedOutput limit in tokens for every call. At least 1, and below ContextWindow when that is set
Thinkingenum?model defaultOff, Low, Medium, High or Max. Which levels work depends on the provider and model, see Output limit and thinking level

Modalities are one comma-separated string, not a JSON array. An omitted optional field means “detect it at runtime”. netclaw writes no placeholder for “cleared”.

Precedence for the context window, highest first:

  1. ContextWindow on the definition
  2. The value the provider reports
  3. 32,768 tokens

Modalities follow the same order, ending at text-only. Runtime detection never writes back to your config (since 0.25.3), and netclaw model discover doesn’t rewrite a definition either. Overrides exist only because you set them.

Detection asks, in order: the Ollama /api/show endpoint (Ollama providers), the OpenAI-compatible /v1/models and llama.cpp /props endpoints (openai-compatible providers), the OpenRouter catalog, then Hugging Face. Results merge per field, and the first answer for a field wins. Each resolver gets 5 seconds. See Self-hosted providers for what each backend reports.

Check what netclaw decided:

Terminal window
netclaw status
model: demo-model (provider: lab, context: 131,072 tokens)
input: Text, Image
output: Text

When detection gets it wrong, override it on the definition:

Terminal window
netclaw model set main remote-gpu qwen3-vl:32b --context-window 65536 --input-modalities "Text, Image"

--clear-context-window and --clear-modalities hand a field back to detection. They don’t restore a value you cleared.

SymptomCauseFix
[compaction] 4 → 3 messages (keep=2, context=10297/8192 tokens) on every turnContextWindow is smaller than netclaw’s own first-turn prompt, so compaction fires constantlyRaise ContextWindow to what the server really serves, or clear it
Context window exceeded after compaction in the errorContextWindow is larger than the server’s real context (for llama.cpp, --ctx-size), so the server rejects the requestLower ContextWindow to the server’s value
config on disk not applied: Invalid model configuration: Models:Definitions:<name>:ContextWindow (2000) is below minimum (4096) in netclaw status, or a startup failure with the same messageOverride below the 4,096 floorRaise it. The floor applies to role-bound definitions only
netclaw status and netclaw model discover disagreeThey read different sources: status shows what the running daemon resolved, discover lists what the provider saysTrust status, and pin ContextWindow if the provider’s number is wrong

A ContextWindow above what the provider reports logs a warning and netclaw uses your number anyway. If the number is too big, the provider rejects the oversized request.

If the model can see images but netclaw status shows input: Text, detection found no modality data. The daemon log says so: resolved to text-only input; no modality data came from the provider or capability oracles. Set --input-modalities "Text, Image" as above. Images and other file types covers what happens next.

A definition can carry an output limit and a thinking level. Set them with netclaw model set, or press P in the Model Manager:

Terminal window
netclaw model set main my-anthropic claude-opus-5-5 --max-output-tokens 64000 --thinking high

--clear-max-output-tokens and --clear-thinking remove a setting. netclaw model list shows both in the Max out and Thinking columns.

  • Default limit. With no MaxOutputTokens, a hosted API gets 32000, capped to the model’s known maximum and clamped to the context window minus the estimated prompt and tool schemas. Anthropic replies used to stop at 1024 tokens and no longer do, so the cost per call can go up.
  • Self-hosted servers. The same clamp applies only when the window is known, from ContextWindow or the server’s own probe. Otherwise netclaw sends no limit and the server decides. A self-hosted server must use the ollama or openai-compatible type. A hosted type such as deepseek pointed at your own server is treated as hosted.
  • Thinking counts against the limit. A low limit with a high level can cut the reply short.
  • Cut-off replies fail the turn. A reply that hits the limit with no tool call ends the turn with The model reply was truncated. The partial text isn’t posted.
  • Levels are never rounded. model set offers only the levels the provider dialect can send and the model accepts, and rejects anything else with the list it would accept. DeepSeek has no medium. Ollama and the ReasoningEffort dialect have no max. When the probe can’t tell what a model supports, only off is offered, and a hosted API with the native dialect offers none.
  • One definition, one setting. Two roles that share a definition change together. Make a second definition of the same model for different settings per role.

An openai-compatible provider needs a thinking dialect before it can send a level. netclaw model set saves ReasoningEffort when it detects llama.cpp. For vLLM, SGLang or a proxy, set it by hand. Values are ChatTemplateKwargs, ReasoningEffort, DeepSeek and None:

{
"Providers": {
"local": {
"Type": "openai-compatible",
"Endpoint": "http://llm.example.internal:8080/v1",
"VendorOptions": { "ThinkingDialect": "ReasoningEffort" }
}
},
"Models": {
"Definitions": {
"main": {
"Provider": "local",
"ModelId": "qwen3:30b",
"ContextWindow": 32768,
"MaxOutputTokens": 4000,
"Thinking": "High"
}
},
"Roles": { "Main": "main" }
}
}

A hand-edited level the dialect can’t send fails startup and netclaw doctor, and doctor --fix doesn’t change it. So does any level but off on an Ollama provider with VendorOptions:DisableThinking set to true.

With a Fallback configured, netclaw tries Main first, then Fallback, for every model call.

First comes retrying. A transient error is retried up to 3 times with exponential backoff (1 second base, 30 seconds max, plus or minus 25% jitter). Retried: network failures, HTTP 408, 429, and 5xx, and timeout exceptions.

Failover starts after that. Any error still standing, including a 400 or a 401 that retrying skips, moves the call to Fallback. The two cases that don’t fail over:

  • A failure after the stream has produced real output (text or a tool call). Splicing two models’ output together would produce garbage. Keepalives and other updates with no content don’t count, so a failure before the first real output still retries and fails over, including on providers that speak the OpenAI Responses protocol.
  • A session watchdog timeout, because the session cancels the call itself rather than the provider failing it.
What you seeMeaning
provider.failover alert (Warning)Main failed, Fallback took the call
provider.unreachable alert (Critical)Every candidate failed. With no Fallback, Main’s failure alone raises this
LLM provider unreachable in the daemon logSame, one candidate

Every role tries the Fallback model last when one is configured, Compaction and child agents included. A Compaction model that is the same as Main, or unset, simply reuses the Main list.

Local models hit time limits before cloud ones do. These live under Session in netclaw.json, in seconds:

KeyDefaultCovers
Session.PrefillTimeoutSeconds1800Waiting for the first token: queue wait plus prompt processing
Session.FirstTokenTimeoutSeconds600The gap between streamed tokens once output has started. Sub-agent runs use it too
Session.NoProgressTimeoutSeconds1200Total time without real output. Backend keepalives don’t reset it. A call that streams only thinking stops at three times this value
Session.SidecarLlmTimeoutSeconds90Titles and memory extraction. Compaction gets twice this plus 5 seconds
{
"Session": {
"SidecarLlmTimeoutSeconds": 300,
"NoProgressTimeoutSeconds": 2400
}
}

The 90-second sidecar budget is the usual first casualty: a slow GPU summarizing a long context hits Context compaction timed out. The session will continue. Raise SidecarLlmTimeoutSeconds. A turn that outlasts the main budgets fails with Session processing operation 'LlmCall' exceeded watchdog timeout of <n>s. Every key is in the configuration reference.

netclaw stats shows token totals (in and out) since the daemon started, with turns and sessions. Add --days N for a daily table. Headless netclaw chat -p prints a [usage] line per reply. Hosted providers’ prices show in netclaw model discover where the provider publishes them.

netclaw has no token budgets, spend caps, or per-user quotas, and no dollar totals. Set limits at the provider: an OpenRouter key limit, or a spend cap in your Anthropic or OpenAI console. Session.MaxToolIterationsPerTurn is a legacy key and no longer limits a turn. A turn that keeps doing new work can run past 60 rounds. netclaw refuses a call that repeats the same work, and the watchdog timeouts above bound the rest.

Override any field with NETCLAW_ variables. Double underscores separate path segments, following the .NET convention:

Terminal window
export NETCLAW_Models__Definitions__claude__Provider="openrouter"
export NETCLAW_Models__Definitions__claude__ModelId="anthropic/claude-sonnet-4"
export NETCLAW_Models__Roles__Main="claude"

Environment variables beat netclaw.json, and Linux names are case-sensitive. A shell can’t export a name containing a hyphen, so use letters, digits, and underscores for definition names, or set the variable through docker run -e, Compose, or a systemd unit, which accept hyphens.

Two quirks of env-only setups. netclaw model list reads netclaw.json only, so an environment-only config shows No models configured. while the daemon runs fine, and netclaw status shows the live model. Without netclaw.json, netclaw doctor skips the checks that read the file, but its Chat Client check still evaluates the environment config, so it does see your model (what doctor skips). The CLI also refuses chat and status with daemon not configured when netclaw.json doesn’t exist, so create one containing {}.

A bad Models or Providers section never takes down a running daemon. It keeps the configuration it already has, logs the reason, and netclaw status shows it:

overall: degraded
config on disk not applied: Models:Roles:Main references unknown definition 'nope'. Defined: d. Fix the name, or run `netclaw model set main <provider> <model>`.
The daemon is still running the previous configuration. No change from netclaw.json is applied until this is fixed.

Fix the file and the daemon picks it up. netclaw doctor fails the Chat Client check with the same message. The trouble comes at the next start, which happens on a reboot, an upgrade, or docker restart. A daemon that starts on a bad file stops with a one-line error and exits.

ConditionResult
Main Provider or ModelId empty, or Main names a provider that isn’t in ProvidersDaemon boots degraded, with the No valid model configuration detected. banner on every turn
Fallback or Compaction incompleteNot applied: model 'Fallback' is configured but incomplete (Provider and ModelId are required). Startup fails
ContextWindow below 4,096 on a role-bound definitionNot applied: ContextWindow (2000) is below minimum (4096). Startup fails
A role names a definition that doesn’t existNot applied: Models:Roles:Main references unknown definition 'x'. Defined: d. Startup fails
A role-bound provider has an unknown TypeNot applied: Unknown provider type 'nosuch'. Supported: .... Startup fails
A role-bound provider has no credentialNot applied: Provider type 'anthropic' requires authentication. Configure ApiKey or OAuthAccessToken in secrets.json. Startup fails
Definitions without RolesNot applied: Models:Definitions is set but Models:Roles is missing or empty. Startup fails
Inline roles mixed with Definitions/RolesNot applied: pick one shape. Startup fails
MaxOutputTokens not below ContextWindow, or a Thinking level the provider dialect can’t sendNot applied: Models:Definitions:main:MaxOutputTokens (40000) must be below the context window (32000). Startup fails
Two definition names differing only by caseThe daemon keeps the running config and netclaw status shows config on disk not applied: Couldn't read netclaw.json: A duplicate key 'Models:Definitions:dup:Provider' was found. Every other command prints that line and exits 1
Unknown role name in Rolesnetclaw doctor fails Config Schema. The daemon ignores the extra role

Under the Docker image a startup failure repeats: the entrypoint restarts the daemon with a growing delay (up to 60 seconds), and docker logs shows the one-line error: ... message each time. The daemon log in ~/.netclaw/logs/ has the same text after Daemon startup stopped:. Unreferenced definitions and providers aren’t validated. netclaw model set doesn’t check that a model ID exists on the provider, so a typo fails on the first turn.

The unknown-definition case is easy to cause by renaming a definition and forgetting a role. netclaw model list and netclaw doctor both name the broken reference.

The daemon restarts itself after a valid edit to netclaw.json. Changes from NETCLAW_Models__* variables or secrets.json need a manual restart: netclaw daemon stop then netclaw daemon start on a host, systemctl --user restart netclaw under systemd, or docker restart <container>.

Terminal window
netclaw model list # reads the config file
netclaw status # what the running daemon is using