Skip to content

Subagents miss Ollama credentials and silently fall back to cloud models (privacy regression) #43945

@Meli73

Description

@Meli73

Summary

When using Ollama providers (e.g. ollama-remote for a remote Ollama instance), the configured apiKey value (typically "ollama-local") is classified as a marker (non-secret placeholder) by the auth pipeline. As a result, it is not written to the agent-level auth-profiles.json.

The main session works correctly because it reads apiKey directly from openclaw.json. However, subagents rely exclusively on auth-profiles.json for credential lookup — and find no entry for the Ollama provider. This causes a 401 auth error, triggering a silent fallback to the next model in the fallback chain (typically a cloud model like openai/gpt-4.1-mini).

The user receives a valid response and has no indication that their prompt was routed to a cloud provider instead of the intended local model.

Security & Privacy Impact

This is a privacy regression with real-world consequences:

  • Users choose local Ollama models specifically for data sovereignty — sensitive prompts, proprietary code, medical data, or personal information should never leave the local network.
  • The silent fallback bypasses user intent and privacy guarantees without any warning, notification, or log entry visible to the end user.
  • Subagent tasks may contain context from the main session (file contents, user data, conversation history) that the user explicitly intended to keep local.
  • The fallback is invisible in normal usage — only deep gateway log analysis (model-fallback/decision entries in /tmp/openclaw/openclaw-*.log) reveals what happened.
  • Risk of targeted exploitation: A malicious actor with knowledge of this behavior could craft scenarios that trigger auth failures on local providers, forcing sensitive data to route through cloud APIs.

This means: a user who believes their data stays local may unknowingly send it to OpenAI, Google, or other cloud providers.

Steps to Reproduce

  1. Configure an Ollama remote provider in openclaw.json:
{
  "models": {
    "providers": {
      "ollama-remote": {
        "baseUrl": "http://192.168.178.122:11434",
        "apiKey": "ollama-local",
        "api": "ollama",
        "models": [{ "id": "qwen3.5:27b", "name": "Qwen 3.5 27B" }]
      }
    }
  },
  "agents": {
    "defaults": {
      "models": { "ollama-remote/qwen3.5:27b": {} },
      "model": { "fallbacks": ["openai/gpt-4.1-mini"] }
    }
  }
}
  1. Restart the gateway
  2. Check auth-profiles.json:
    cat ~/.openclaw/agents/main/agent/auth-profiles.json | jq '.profiles | keys'
    # → ["anthropic:default", "xai:default"] — no ollama-remote entry
  3. Run openclaw models status:
    ollama-remote effective=missing:missing | models.json=marker(ollama-local)
    
  4. Spawn a subagent targeting the Ollama model
  5. Observed: Subagent completes in ~3-4 seconds, claims to be "GPT-4.1-mini"
  6. Expected: Subagent uses the local Ollama model (slower, ~30s+), or fails with a clear error if the local model is unavailable

Root Cause Analysis

Affected components: Gateway auth pipeline (auth-profiles writer/sync), subagent credential resolver, model fallback engine.

The auth pipeline has a marker detection mechanism that classifies certain API key values (like "ollama-local", "OPENAI_API_KEY", etc.) as non-secret placeholders. These markers are intentionally excluded from auth-profiles.json to prevent example/dummy keys from polluting the secret store.

However, for Ollama providers, the "marker" value IS the intended credential — Ollama doesn't require real authentication. The marker filter doesn't distinguish between "placeholder that should be replaced" and "intentionally simple key for an auth-free service."

The credential resolution chain for subagents:

  1. Read from openclaw.json directly (not available to subagents)
  2. Look up in auth-profiles.jsonempty for Ollama → 401
  3. Check environment variables → not propagated to subagent lanes
  4. Fallback to next model (cloud) → silent success

Additionally, the gateway regenerates auth-profiles.json on every restart, overwriting any manual fixes.

Log Evidence

[ERROR] FailoverError: No API key found for provider "ollama-remote".
  Auth store: ~/.openclaw/agents/main/agent/auth-profiles.json
  
[WARN] model_fallback_decision: candidate_failed
  requestedProvider: ollama-remote
  requestedModel: qwen3.5:27b  
  reason: auth, status: 401
  nextCandidate: openai/gpt-4.1-mini

[WARN] model_fallback_decision: candidate_succeeded
  candidateProvider: openai
  candidateModel: gpt-4.1-mini
  previousAttempts: [{provider: ollama-remote, reason: auth, status: 401}]

Note: These entries are only visible in the gateway file log (/tmp/openclaw/openclaw-*.log), not in any user-facing UI or dashboard. Users have no way to detect this behavior without manually inspecting logs.

Proposed Fixes (ordered by priority)

Fix 1: Propagate all provider apiKeys to auth-profiles.json (minimal, quick win)

Write all models.providers.*.apiKey values to auth-profiles.json, regardless of marker classification. The auth store should reflect the config, not second-guess it. If a user configured a key, it should be available to all sessions including subagents.

Affected: Gateway auth-profiles writer/sync logic.

Fix 2: Skip auth lookup for auth-free providers (architectural)

Introduce an auth: "none" provider capability. When api: "ollama" is set (or explicitly configured), the auth pipeline should be bypassed entirely — no auth-profiles lookup needed. Ollama doesn't use authentication; the current system forces users to provide a dummy key just to satisfy the pipeline.

This would also benefit other local/auth-free providers like LM Studio, vLLM, llama.cpp server, and similar local inference backends.

Affected: Provider auth resolver, auth pipeline.

Fix 3: Add a fail-closed policy flag for local-only routing (privacy)

Add a per-agent or per-provider policy flag like allowCloudFallback: false or requireLocal: true. When set, the fallback mechanism must never cross the local→cloud boundary silently. If the local model fails, the task should fail with a clear error rather than leak data to a cloud provider.

Consider how this interacts with existing fallback chains — the flag should override automatic fallback behavior when privacy is at stake.

Affected: Model fallback engine, agent config schema.

Fix 4: Surface fallback decisions to the user (transparency)

When a model fallback crosses a provider boundary (especially local→cloud), emit a user-visible warning — not just a log entry buried in /tmp. The user should always know when their intended model was substituted. This could be a notification in the chat, a dashboard alert, or at minimum a clearly visible log line.

Affected: Model fallback engine, notification/messaging system.

Fix 5: Add CI/integration tests for auth-profiles behavior (prevention)

Establish automated tests that verify auth-profiles.json is correctly populated for all provider types — especially local providers with marker-style keys. This prevents regressions in the auth subsystem when future changes are made.

Affected: Test suite, CI pipeline.

Workaround

  1. Manually add Ollama entries to auth-profiles.json:
{
  "ollama-remote:default": {
    "type": "api_key",
    "provider": "ollama-remote",
    "key": "sk-ollama-dummy"
  }
}
  1. ⚠️ Protect the file from being overwritten on restart:
chmod 444 ~/.openclaw/agents/main/agent/auth-profiles.json

⚠️ Important caveats:

  • The key value must NOT be "ollama-local" — it gets re-classified as a marker. Use any non-marker string like "sk-ollama-dummy".
  • The chmod 444 prevents the gateway from updating auth-profiles.json for ANY provider — adding or rotating keys for other providers (Anthropic, OpenAI, etc.) will silently fail until the file is made writable again.
  • After openclaw update, verify the file still contains the Ollama entries — updates may regenerate the file.
  • This is a dirty hack, not a sustainable solution.

Related Issue

Ollama model cold-start causes timeout fallback (see #43946)

Even with auth fixed, Ollama models need ~13 seconds to load on first request. The default LLM request timeout appears too short for cold-start scenarios, causing a secondary timeout-based fallback (status 408). Pre-loading models via curl /api/generate with keep_alive works around this, but a configurable requestTimeout per provider or model would be the proper fix.

Environment

  • OpenClaw 2026.3.11
  • Ollama 0.17.7 (remote, Windows + local, Linux)
  • Both ollama-local and ollama-remote providers affected
  • Issue likely affects all Ollama provider configurations used in subagent context

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions