{"record":{"id":"18476d581c129588","repo":"cocoindex-io/cocoindex","slug":"environment-provide-type-has-already-been-p","errorCode":null,"errorMessage":"Environment::provide: type `{}` has already been provided","messagePattern":"Environment::provide: type `(.+?)` has already been provided","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"rust/sdk/cocoindex/src/app.rs","lineNumber":154,"sourceCode":"    pub fn lmdb_map_size(mut self, value: usize) -> Self {\n        self.lmdb_map_size = value;\n        self\n    }\n\n    /// Limit the number of concurrently processing components (per app).\n    pub fn max_inflight_components(mut self, value: usize) -> Self {\n        self.max_inflight_components = Some(value);\n        self\n    }\n\n    /// Inject a shared resource. Retrieved later via `ctx.get::<T>()`.\n    /// The type IS the key — each type can only be provided once.\n    ///\n    /// # Panics\n    /// Panics if a value of type `T` has already been provided.\n    pub fn provide<T: Send + Sync + 'static>(mut self, value: T) -> Self {\n        if self.state.contains::<T>() {\n            panic!(\n                \"Environment::provide: type `{}` has already been provided\",\n                std::any::type_name::<T>()\n            );\n        }\n        self.state.insert(value);\n        self\n    }\n\n    /// Inject a shared resource by named [`ContextKey`].\n    ///\n    /// Named keys are useful when multiple resources share the same Rust type\n    /// and carry change-tracking.\n    ///\n    /// # Panics\n    /// Panics if a change-tracked key cannot fingerprint the provided value.\n    pub fn provide_key<T: Send + Sync + 'static>(mut self, key: &ContextKey<T>, value: T) -> Self {\n        self.context\n            .provide(key, value)","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/rust/sdk/cocoindex/src/app.rs#L136-L172","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nenv.provide(pool).provide(pool2) // both asyncpg::Pool\n\n// after\nstruct ReplicaPool(asyncpg::Pool);\nenv.provide(pool).provide(ReplicaPool(pool2))","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// guard before providing\nfn can_provide<T: Send + Sync + 'static>(env: &EnvironmentBuilder, _v: &T) -> bool {\n    !env.contains::<T>()\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["rust","panic","environment","dependency-injection"],"backgroundTag":"invalid-state-transition","analyzedSha":"e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b","analyzedAt":"2026-09-08T15:59:19.997Z","contentChangedAt":"2026-09-08T15:59:19.997Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}