{"record":{"id":"649bfc1d0c5a7789","repo":"tinyhumansai/openhuman","slug":"local-only-privacy-mode-is-active-this-action-nee-649bfc","errorCode":null,"errorMessage":"Local-only privacy mode is active: this action needs external service `{}`. Disable local-only mode in Settings to allow it.","messagePattern":"Local-only privacy mode is active: this action needs external service `(.+?)`\\. Disable local-only mode in Settings to allow it\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/security/egress/enforce.rs","lineNumber":163,"sourceCode":"}\n\n/// Enforce the live privacy policy for an `anyhow`-returning egress site\n/// (composio tool calls, backend integrations, cloud embeddings). Reads the live\n/// mode (defaulting to `Standard`/allow when no session policy is installed) and\n/// returns `Err(block_message)` when [`local_only_blocks`] refuses the transfer,\n/// else `Ok(())`. Call this **before** the disclose-and-send (i.e. before\n/// [`emit_external_transfer`](super::emit::emit_external_transfer)) so a blocked\n/// transfer is neither disclosed as pending nor dispatched.\npub fn enforce_egress(desc: &EgressDescriptor) -> anyhow::Result<()> {\n    let mode = current_privacy_mode();\n    if local_only_blocks(mode, desc) {\n        log::warn!(\n            \"[privacy][egress-enforce] LocalOnly BLOCK provider={} service={} reason={:?} — refused\",\n            desc.provider_slug,\n            desc.service,\n            desc.reason,\n        );\n        anyhow::bail!(\"{}\", block_message(desc));\n    }\n    log::debug!(\n        \"[privacy][egress-enforce] privacy_mode={:?} provider={} service={} reason={:?} — permitted\",\n        mode,\n        desc.provider_slug,\n        desc.service,\n        desc.reason,\n    );\n    Ok(())\n}\n\n/// Enforce the live privacy policy for an agent tool whose `execute` returns\n/// `Ok(ToolResult::error(..))` on a denied action (the network tools). Returns\n/// `Some(message)` — prefixed with [`POLICY_BLOCKED_MARKER`] so the agent loop\n/// treats it as a hard, cross-turn policy block (no pointless retries) — when the\n/// transfer is refused, else `None`. Call it before the disclose-and-send; on\n/// `Some`, short-circuit with `Ok(ToolResult::error(message))`.\npub fn local_only_tool_block(desc: &EgressDescriptor) -> Option<String> {","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/security/egress/enforce.rs#L145-L181","documentation":"enforce_egress blocks an outbound transfer because the session's privacy mode is local-only: the action would contact an external service, which local-only forbids. It fires before disclose-and-send so a refused transfer is neither reported as pending nor dispatched; the mode defaults to allow when no policy is installed, so this only fires under an explicit local-only session policy.","triggerScenarios":"Thrown at src/openhuman/security/egress/enforce.rs:163 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Disable local-only mode in Settings if external access is intended.","Choose a local/offline alternative for the action.","Ask the user to relax the privacy policy for this session."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}