cocoindex-io/cocoindex · warning
Overriding the default lifespan function
Error message
Overriding the default lifespan function {self._lifespan_fn} with {fn}. What it means
Environment.lifespan() registers the lifespan function; if one was already registered, the old one is overwritten and a warning (not an error) is emitted showing both functions. This usually means two parts of the program each tried to set the app's lifespan, so one provider silently stops being used.
Solutions
- Register the lifespan only once per Environment; consolidate the two functions into one.
- Compose lifespans: call the first function inside the second (or use a stacking helper) instead of overwriting.
- If overwriting is intentional, suppress/handle the warning explicitly and document why.
- Ensure setup helpers are idempotent (guard with a flag) so lifespan isn't set twice.
Example fix
# before
env.lifespan(setup_db)
env.lifespan(setup_cache) # overwrites setup_db
# after
async def combined():
await setup_db()
await setup_cache()
env.lifespan(combined) Defensive patterns
Strategy: validation
Validate before calling
assert env._lifespan_fn is None, "lifespan already registered; compose instead of overriding"
Try / catch
import warnings
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
env.lifespan(fn)
if any("Overriding the default lifespan" in str(x.message) for x in w):
raise RuntimeError("lifespan registered twice — compose the functions instead") Prevention
- Register lifespan exactly once per Environment, ideally in a single bootstrap function.
- Compose multiple setup steps into one lifespan function.
- Treat this warning as an error in CI with `-W error::UserWarning` filters.
When it happens
Trigger: Calling env.lifespan(fn) twice on the same Environment — e.g. a library sets a default lifespan and app code sets another, or a setup helper is invoked twice (common with test fixtures and app re-initialization).
Common situations: Combining cocoindex helpers that internally call lifespan() with your own lifespan registration; running tests where a module-level setup and a per-test fixture both configure the same Environment; duplicated app bootstrap code after a refactor.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- An app named ' ' is already registered in this environment.
- aiobotocore is required to use the Amazon S3 source…
- () is not allowed inside process_live; use operator.update…
- Application file path not found
- Async functions are not supported by @coco.fn decorator…
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/70d758a5034cad54.
Report an issue: GitHub.
Appendix: source
Thrown at python/cocoindex/_internal/environment.py:332
self._lifespan_fn = None
self._exit_stack = None
self._env = None
self._info = EnvironmentInfo(self)
def _get_start_stop_lock(self) -> asyncio.Lock:
"""Get or create the start/stop lock (must be called from async context)."""
if self._start_stop_lock is None:
self._start_stop_lock = asyncio.Lock()
return self._start_stop_lock
@property
def name(self) -> str:
return self._name
def lifespan(self, fn: LifespanFn) -> None:
with self._lifespan_fn_lock:
if self._lifespan_fn is not None:
warnings.warn(
f"Overriding the default lifespan function {self._lifespan_fn} with {fn}."
)
self._lifespan_fn = fn
async def _reset(self) -> None:
await self.stop()
with self._lifespan_fn_lock:
self._lifespan_fn = None
async def _get_env(self) -> Environment:
"""
Start the default environment (executes on the default environment's event loop).
"""
async with self._get_start_stop_lock():
if self._env is not None:
return self._env
with self._lifespan_fn_lock:
fn = self._lifespan_fn or _noop_lifespan_fnView on GitHub (pinned to e84aa99b32)