Common Pitfalls When Embedding OpenHuman as a Library Using the Harness API
Embedding OpenHuman via the Harness API requires careful configuration of product identity, backend URLs, access tiers, and workspace isolation to avoid runtime failures ranging from silent header omissions to process singleton collisions.
OpenHuman's Rust core exposes a powerful embedding interface through the Harness builder defined in src/embed/harness/builder.rs. While this API enables deep integration of agentic capabilities into external applications, several architectural constraints can trigger subtle failures if ignored. Understanding these pitfalls ensures reliable operation when embedding OpenHuman as a library.
Missing Product Identity Causes Silent Header Omissions
The backend SDK in src/api/product.rs relies on a global ProductIdentity to tag outgoing requests with the x-sdk-name header. If you instantiate a BackendOAuthClient before calling set_product_identity, the header remains unset, preventing the backend from attributing requests to your application.
How to avoid it: Set the product identity immediately at application startup, before any client construction:
use openhuman::api::{set_product_identity, ProductIdentity};
// Must occur before BackendOAuthClient creation
set_product_identity(ProductIdentity::new("myapp").unwrap());
Backend URL Misconfiguration Triggers Session Expired Errors
Many core operations—including credential refresh and analytics pings—hit the TinyHumans backend regardless of your inference provider. If backend_url is omitted or unreachable, these calls fail with SessionExpired errors even when the primary LLM provider is operational.
Configuration location: src/embed/harness/builder.rs
Always specify a reachable backend during harness construction:
let harness = Harness::builder()
.backend_url("https://api.tinyhumans.com".into())
// ... additional config
.build()
.await
.expect("Failed to init Harness");
Access and Autonomy Misconfiguration Blocks Operations
The Access struct in src/embed/harness/access.rs defines permission tiers, but the accompanying Autonomy settings determine whether the core treats the current turn as originating from a trusted source. Default configurations or missing Autonomy settings result in the approval gate refusing writes, network calls, or tool installations.
The fix: Explicitly configure both components:
use openhuman::embed::harness::{Access, Autonomy};
let harness = Harness::builder()
.access(Access::full())
.autonomy(Autonomy::default()) // Ensures trusted root behavior
// ...
.build()
.await?;
Default Workspace Directories Leak Credentials
The Workspace controller in src/embed/harness/workspace.rs determines where OpenHuman stores persistent state, credentials, and keyring data. Using the default path (typically under ~/.openhuman) while expecting an isolated environment leads to unexpected credential sharing between distinct application instances.
Isolation strategies:
- Ephemeral runs: Use
Workspace::Ephemeralfor stateless, one-off executions - Dedicated directories: Use
Workspace::Dir(PathBuf::from("./my_workspace"))for persistent but isolated storage
Provider Gate Failures Without Session Activation
Configuring a Provider in src/embed/harness/provider.rs sets the endpoint and credentials, but the core additionally verifies that the session is "active" via verify_session_active. If you specify the provider but omit the session initialization, the gate rejects operations.
Complete provider setup:
use openhuman::embed::harness::{Provider, Session};
let harness = Harness::builder()
.provider(Provider::openai_compatible(
"https://api.openai.com/v1".into(),
"sk-my-key".into(),
).model("gpt-4o"))
.session(Session::local("my-embed")) // Required for gate verification
// ...
.build()
.await?;
Neglected MCP Configuration Breaks Tool Calls
MCP servers are optional components defined in src/embed/harness/mcp.rs. If your application enables tools that rely on a local MCP server but you never start the server (or forget to add it to the builder), RPC calls return UNKNOWN_METHOD errors rather than graceful degradation.
Singleton Constraint Prevents Multiple Harness Instances
OpenHuman maintains process-wide singletons for the keyring master key, RPC bearer token, and event bus. Attempting to create a second Harness instance via Harness::builder().build() yields HarnessError::AlreadyRunning as defined in src/embed/harness/error.rs.
Pattern for reuse: Maintain a single static instance throughout the application lifecycle:
use std::sync::OnceCell;
static HARNESS: OnceCell<Harness> = OnceCell::new();
async fn get_harness() -> &'static Harness {
HARNESS.get_or_init(|| async {
Harness::builder()
.provider(/* ... */)
.backend_url(/* ... */)
.build()
.await
.expect("Failed to create harness")
}.await)
}
Tokio Runtime Stack Overflow Risks
Background services and tool timeout mechanisms in src/embed/call.rs spawn Tokio tasks. Using the default #[tokio::main] configuration without adjusting AGENT_WORKER_STACK_BYTES can cause stack overflow during nested sub-agent calls.
Environment configuration:
export AGENT_WORKER_STACK_BYTES=4194304 # 4MB or larger for deep recursion
Then launch your application within this configured runtime.
Cargo Feature Gate Omissions Remove Tools Silently
OpenHuman uses Cargo feature gates (e.g., voice, web3, flows) defined in src/core/all.rs to conditionally compile domain controllers. Relying on a tool that lives behind a disabled feature gate results in the tool being omitted from the binary entirely, without runtime error indication.
Dependency configuration: Enable required features in your Cargo.toml:
[dependencies]
openhuman = { git = "https://github.com/tinyhumansai/openhuman", features = ["flows", "web3"] }
Complete Working Example
The following demonstrates a properly configured harness that avoids the common pitfalls:
use openhuman::embed::harness::*;
use openhuman::api::{set_product_identity, ProductIdentity};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Set product identity before any client construction
set_product_identity(ProductIdentity::new("myapp").unwrap());
// 2. Configure stack size if using nested sub-agents
std::env::set_var("AGENT_WORKER_STACK_BYTES", "4194304");
// 3. Build harness with all required guards
let harness = Harness::builder()
.provider(Provider::openai_compatible(
"https://api.openai.com/v1".into(),
"sk-my-key".into(),
).model("gpt-4o"))
.backend_url("https://api.tinyhumans.com".into())
.workspace(Workspace::Dir("./isolated_workspace".into()))
.access(Access::full())
.autonomy(Autonomy::default())
.session(Session::local("my-embed"))
.services(ServiceSet::desktop())
.domains(DomainSet::full())
.build()
.await?;
// 4. Execute turn
let reply = harness
.run("Analyze this codebase structure.")
.await?;
println!("Result: {}", reply.content);
// 5. Reuse harness for subsequent operations (do not rebuild)
let follow_up = harness
.turn("List any security issues found.")
.session(&reply.session_id)
.send()
.await?;
println!("Follow-up: {}", follow_up.content);
Ok(())
}
Summary
- Initialize early: Call
set_product_identitybefore anyBackendOAuthClientinstantiation to ensure proper request attribution - Configure completely: Specify
backend_url,Provider,Session,Access, andAutonomyto prevent gate failures - Isolate state: Use
Workspace::Ephemeralor dedicated directories to avoid credential leakage - Respect singletons: Maintain only one
Harnessinstance per process; reuse via static references - Size the runtime: Set
AGENT_WORKER_STACK_BYTESappropriately when using nested sub-agents - Enable features: Explicitly declare required Cargo features (
flows,web3, etc.) inCargo.toml
Frequently Asked Questions
What happens if I forget to set the product identity before creating the harness?
The x-sdk-name header remains unset in backend requests, causing the TinyHumans API to reject or fail to attribute analytics calls. This manifests as authentication or tracking failures in src/api/product.rs because the global ProductIdentity is read once at client construction time.
Can I create multiple Harness instances for different users in the same process?
No. The OpenHuman core maintains process-wide singletons for the keyring, RPC bearer, and event bus as enforced in src/embed/harness/error.rs. Attempting a second build() call yields HarnessError::AlreadyRunning. You must reuse a single instance and manage multi-tenancy through session management rather than separate harnesses.
Why do I get SessionExpired errors when my API key is valid?
This typically indicates a missing or unreachable backend_url. Even when using third-party providers like OpenAI, OpenHuman requires communication with the TinyHumans backend for credential refresh and session management, as implemented in src/embed/harness/builder.rs. Ensure the URL is reachable and correctly configured.
How do I prevent tools from being silently unavailable?
Verify that the Cargo features required for your tools are enabled in your Cargo.toml. Features like flows, web3, and voice gate the inclusion of domain controllers in src/core/all.rs. Without the feature enabled, the tool code is omitted at compile time, causing runtime UNKNOWN_METHOD errors when the agent attempts to invoke them.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →