tinyhumansai/openhuman · warning · anyhow::Error

Local-only privacy mode is active: this action needs externa

Error message

Local-only privacy mode is active: this action needs external provider {label}. Switch to a local model (Ollama/LM Studio/etc.) or change privacy mode in Settings.

What it means

OpenHuman's privacy enforcement chokepoint (`enforce_local_only_inference` / `local_only_violation`, factory.rs:603-651) refused to build an external chat provider because the live privacy policy is `PrivacyMode::LocalOnly`. The resolved provider string is neither empty, the `cloud` sentinel (which defers), nor recognized by `is_local_provider_string` (Ollama/LM Studio/local runtimes), so it is classified as external and blocked. The label names the external destination.

Source

Thrown at src/openhuman/inference/provider/factory.rs:645

    let mode = crate::openhuman::security::live_policy::current_privacy_mode();
    match local_only_violation(mode, provider) {
        None => {
            log::debug!(
                "[privacy][chat-factory] privacy_mode={:?} role={} provider='{}' — inference permitted",
                mode,
                role,
                provider.trim()
            );
            Ok(())
        }
        Some(label) => {
            log::warn!(
                "[privacy][chat-factory] LocalOnly BLOCK: role={} external provider='{}' ({}) refused",
                role,
                provider.trim(),
                label
            );
            anyhow::bail!(
                "Local-only privacy mode is active: this action needs external provider {label}. \
                 Switch to a local model (Ollama/LM Studio/etc.) or change privacy mode in Settings."
            )
        }
    }
}

/// Egress spine (privacy epic S2, #4436): emit an [`EgressDescriptor`] for a
/// concrete inference provider string. `provider` is expected to be already
/// resolved (no `""` / `"cloud"` / BYOK sentinels — those are handled before
/// this is called). Local runtimes are marked non-external, so
/// [`emit_external_transfer`](crate::openhuman::security::egress::emit_external_transfer)
/// discloses them without firing the external-transfer event.
fn emit_inference_egress(role: &str, provider: &str) {
    let p = provider.trim();
    if p.is_empty() || p == "cloud" {
        // Defensive: a sentinel would re-resolve on recursion; don't emit here.
        return;

View on GitHub (pinned to 7491200858)

Solutions

  1. Switch the offending role's provider to a local runtime (Ollama / LM Studio) in Settings, so `is_local_provider_string` accepts it.
  2. Or change privacy mode away from Local-only in Settings if external providers are intended.
  3. Identify which role triggered it from the `[privacy][chat-factory] LocalOnly BLOCK: role=... provider=...` warn log and fix that specific role.
  4. For background workloads, configure them to use a local model or the exempt managed path per your privacy policy.

Example fix

# config: point the role at a local runtime instead of a cloud slug
# before
summary_provider = "anthropic:claude-sonnet-4-6"
# after
summary_provider = "ollama:llama3.1:8b"
Defensive patterns

Strategy: validation

Validate before calling

// Before requesting a workload on an external provider:
use crate::openhuman::config::PrivacyMode;
use crate::openhuman::inference::local::profile::is_local_provider_string;
if crate::openhuman::security::live_policy::current_privacy_mode() == PrivacyMode::LocalOnly
    && !is_local_provider_string(provider_string)
{
    return local_only_notice(); // don't attempt the call
}

Type guard

fn provider_allowed_under_local_only(provider: &str) -> bool {
    let p = provider.trim();
    p.is_empty() || p == "cloud" || is_local_provider_string(p)
}

Prevention

When it happens

Trigger: Any chat/inference request whose role resolves to an external provider string (e.g. `anthropic:claude-...`, `openai:...`) while the session's live privacy mode is LocalOnly. Fired from `enforce_local_only_inference` at the factory chokepoint before the provider is constructed.

Common situations: User enabled Local-only privacy mode in Settings then a background workload (summaries, embeddings, a cron role) still points at a cloud provider; a shared/parental-control profile with a local-only policy installed; switching a role to a cloud model without noticing the mode.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/b41fe5a099426b1d. Report an issue: GitHub.