Skip to content

Startup crash: relative LANGFLOW_DATABASE_URL silently uses wrong path when CWD ≠ project root, then cleanup crashes with UnboundLocalError: temp_dirs #13634

@kartikgajjar

Description

@kartikgajjar

Bug Description

When LANGFLOW_DATABASE_URL is set to a relative SQLite path (e.g. sqlite:///db/langflow.db) and langflow run is startedfrom a directory other than the project root, two bugs fire in sequence:

  1. SQLAlchemy resolves the relative path against the shell's CWD, not the project root, so it tries to open/create a DBat the wrong location and fails.
  2. Because startup fails before the bundle-loading step, temp_dirs is never assigned in lifespan(), so the shutdown cleanup crashes with UnboundLocalError: cannot access local variable 'temp_dirs' where it is not associated with a value.

The second crash masks the real error — the user sees two chained exceptions and has to trace back to understand that the actual cause was a path resolution issue.

Reproduction

.env in project root:

LANGFLOW_DATABASE_URL=sqlite:///db/langflow.db

cd /some/other/directory
langflow run --env-file /path/to/.env


Observed Behaviour

RuntimeError: Error creating DB and tables

During handling of the above exception, another exception occurred:

UnboundLocalError: cannot access local variable 'temp_dirs' where it is not
associated with a value

Full traceback (condensed):
langflow/main.py:198 await initialize_services(fix_migration=fix_migration)
langflow/services/utils.py:602 await initialize_database(fix_migration=fix_migration)
langflow/services/database/utils.py:34 raise RuntimeError(msg) from exc
RuntimeError: Error creating DB and tables

During handling of the above exception, another exception occurred:

langflow/main.py:588
temp_dir_cleanups = [asyncio.to_thread(temp_dir.cleanup) for temp_dir in temp_dirs]
UnboundLocalError: cannot access local variable 'temp_dirs' where it is not associated with a value

Expected behavior

  • A relative SQLite path should be resolved to absolute at service initialisation time (anchored to CWD at startup), making behaviour consistent regardless of which directory langflow run is invoked from.
  • temp_dirs should be initialised to [] before the try block in lifespan() so cleanup never crashes on early startup failures.

Root Cause

services/database/service.py — _sanitize_database_url

The method normalises the driver name (sqlite → sqlite+aiosqlite) but does not touch the path component. SQLAlchemy receives the relative path and resolves it against the process CWD, which may not be the project root.

main.py — lifespan

temp_dirs is only assigned inside the bundle-loading branch (lines ~276–281), which runs after initialize_services. When
During handling of the above exception, another exception occurred:

UnboundLocalError: cannot access local variable 'temp_dirs' where it is not
associated with a value

Full traceback (condensed):
langflow/main.py:198 await initialize_services(fix_migration=fix_migration)
langflow/services/utils.py:602 await initialize_database(fix_migration=fix_migration)
langflow/services/database/utils.py:34 raise RuntimeError(msg) from exc
RuntimeError: Error creating DB and tables

During handling of the above exception, another exception occurred:

langflow/main.py:588
temp_dir_cleanups = [asyncio.to_thread(temp_dir.cleanup) for temp_dir in temp_dirs]
UnboundLocalError: cannot access local variable 'temp_dirs' where it is not associated with a value


Expected Behaviour

  • A relative SQLite path should be resolved to absolute at service initialisation time (anchored to CWD at startup),
    making behaviour consistent regardless of which directory langflow run is invoked from.
  • temp_dirs should be initialised to [] before the try block in lifespan() so cleanup never crashes on early startup
    failures.

Root Cause

services/database/service.py — _sanitize_database_url

The method normalises the driver name (sqlite → sqlite+aiosqlite) but does not touch the path component. SQLAlchemy
receives the relative path and resolves it against the process CWD, which may not be the project root.

main.py — lifespan

temp_dirs is only assigned inside the bundle-loading branch (lines ~276–281), which runs after initialize_services. When
initialize_services raises, execution jumps to the finally block, which tries to iterate temp_dirs before it was ever
set.


Proposed Fix

_sanitize_database_url in service.py:
if driver in {"sqlite", "sqlite+aiosqlite"}:
driver = "sqlite+aiosqlite"
raw_path = url_components[1][1:] # strip the separator slash
db_path = Path(raw_path)
if not db_path.is_absolute():
db_path = Path.cwd() / db_path
self.database_url = f"{driver}:///{db_path.as_posix()}"

lifespan in main.py:
sync_flows_from_fs_task = None
mcp_init_task = None
models_dev_refresh_task = None
temp_dirs = [] # ← initialise before try so cleanup never crashes

try:
...

  1. .venv\Lib\site-packages\langflow\services\database\service.py — _sanitize_database_url: resolves relative SQLite
    paths to absolute using CWD at startup.
  2. .venv\Lib\site-packages\langflow\main.py — lifespan: initialises temp_dirs = [] before the try block.

service.py


Environment

  • Langflow: 1.10.0
  • OS: Windows 11
  • Python: 3.13.7
  • Database: SQLite

Who can help?

main.py

Operating System

Windows 11

Langflow Version

1.10.0

Python Version

3.12

Screenshot

service.py

Flow File

No response

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingjiraThis issue has been logged in Jira for fix by the engineering team.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions