cocoindex-io/cocoindex · critical
Environment::provide: type `{}` has already been provided
Error message
Environment::provide: type `{}` has already been provided What it means
Environment::provide() enforces that each Rust type acts as a unique key in the environment's typed state map. Panicking means you called .provide::<T>() twice for the same type T on the same Environment builder. This is a programmer invariant violation: the second value would silently shadow the first, so the library aborts instead.
Source
Thrown at rust/sdk/cocoindex/src/app.rs:154
pub fn lmdb_map_size(mut self, value: usize) -> Self {
self.lmdb_map_size = value;
self
}
/// Limit the number of concurrently processing components (per app).
pub fn max_inflight_components(mut self, value: usize) -> Self {
self.max_inflight_components = Some(value);
self
}
/// Inject a shared resource. Retrieved later via `ctx.get::<T>()`.
/// The type IS the key — each type can only be provided once.
///
/// # Panics
/// Panics if a value of type `T` has already been provided.
pub fn provide<T: Send + Sync + 'static>(mut self, value: T) -> Self {
if self.state.contains::<T>() {
panic!(
"Environment::provide: type `{}` has already been provided",
std::any::type_name::<T>()
);
}
self.state.insert(value);
self
}
/// Inject a shared resource by named [`ContextKey`].
///
/// Named keys are useful when multiple resources share the same Rust type
/// and carry change-tracking.
///
/// # Panics
/// Panics if a change-tracked key cannot fingerprint the provided value.
pub fn provide_key<T: Send + Sync + 'static>(mut self, key: &ContextKey<T>, value: T) -> Self {
self.context
.provide(key, value)View on GitHub (pinned to e84aa99b32)
Solutions
- Remove the duplicate .provide() call for that type.
- Wrap one of the values in a distinct newtype so each type is unique (struct SecondaryPool(Pool)).
- If the value must be replaced, rebuild the Environment instead of re-providing on the same builder.
Example fix
// before env.provide(pool).provide(pool2) // both asyncpg::Pool // after struct ReplicaPool(asyncpg::Pool); env.provide(pool).provide(ReplicaPool(pool2))
Defensive patterns
Strategy: type-guard
Type guard
// guard before providing
fn can_provide<T: Send + Sync + 'static>(env: &EnvironmentBuilder, _v: &T) -> bool {
!env.contains::<T>()
} Prevention
- Give each provided resource its own newtype so types are unique keys.
- Centralize environment construction in one function to avoid duplicate provides.
- Search the codebase for `provide::<` with the type before adding a second provide.
When it happens
Trigger: Chaining .provide::<Pool>(p1).provide::<Pool>(p2), or providing the same concrete type from two code paths that both build the environment (e.g. a helper called twice).
Common situations: Refactoring two distinct resource types into the same struct/type; calling provide for a type already provided inside a shared lifespan helper; copy-pasted builder chains in tests.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Environment::provide_key({}): {e}
- invalid exclude_reference_patterns regex
- valid tokenizer regex
- with_base_url must be called before the connection is shared
- An app named '{name}' is already registered in this environm
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/18476d581c129588.
Report an issue: GitHub.