Skip to content

A relative [agent] spec in the GLOBAL ~/.langstage/config.toml resolves against ~/.langstage/, not the invocation cwd — a documented global default agent silently 404s (global-config sibling of #116/#132) #133

Description

@dkedar7

Summary

The README documents two interchangeable-looking TOML config locations with the same keys ("Configuration Files": global ~/.langstage/config.toml and project langstage.toml), and its one example uses a relative spec = "my_agent.py:graph". But a relative [agent] spec resolves against a different base directory depending on which file holds it:

Where the same value spec = "my_agent.py:graph" is set Resolves against Result
LANGSTAGE_AGENT_SPEC env var invocation cwd ✅ runs
project langstage.toml project/toml dir (= cwd here) ✅ runs
global ~/.langstage/config.toml ~/.langstage/ (config-home dir) FileNotFoundError

So a user who sets a global default agent using the documented relative-spec format gets, from every project, an error naming a path they never wrote (~/.langstage/my_agent.py) — because no one keeps agent code in ~/.langstage/.

This is the global-config sibling of the #116 (project-spec) / #132 ([workspace] root) relative-resolution family. The #116 fix ("resolve a relative path against the dir containing the toml, like pyproject.toml") is sound for a project config, where the toml dir ≈ where your code lives — but for the global config, ~/.langstage/ is a config dir, never a code dir, so that rule yields a guaranteed-useless path.

Environment

  • langstage-cli 0.6.30, langstage-core 1.0.34
  • Python 3.11, Linux
  • Clean-room pip install langstage-cli from PyPI

Minimal repro

export LANGSTAGE_CONFIG_HOME=/tmp/clean_home   # relocates the global config dir (documented)
rm -rf /tmp/clean_home /tmp/clean_proj && mkdir -p /tmp/clean_home /tmp/clean_proj

# The agent lives where you WORK:
cat > /tmp/clean_proj/my_agent.py <<'PY'
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import MessagesState
from langchain_core.messages import AIMessage
def respond(state): return {"messages":[AIMessage(content="agent ran OK")]}
g=StateGraph(MessagesState); g.add_node("respond",respond)
g.add_edge(START,"respond"); g.add_edge("respond",END)
graph=g.compile()
PY

# GLOBAL config points at it with the documented relative-spec format:
printf '[agent]\nspec = "my_agent.py:graph"\n' > /tmp/clean_home/config.toml

cd /tmp/clean_proj
langstage-cli --no-interactive "hi"          # <-- A) global config
cp /tmp/clean_home/config.toml ./langstage.toml
langstage-cli --no-interactive "hi"          # <-- B) same value, project config

Actual

# A) global config
Error: FileNotFoundError: Agent file not found: /tmp/clean_home/my_agent.py
# exit=1

# B) same value in project langstage.toml
agent ran OK
# exit=0

The agent file is present in the cwd the whole time; only the config location changed.

Expected

One of:

  • A relative [agent] spec in the global config resolves against the invocation cwd (the only sensible base for a cross-project default), matching the env-var behavior; or
  • It is rejected up front with a clear message ("a relative agent spec in the global config is ambiguous — use an absolute path or a module:attr spec"), instead of silently resolving to ~/.langstage/… and 404-ing.

Either way, the current silent resolution against ~/.langstage/ — a directory the user never referenced — is the surprising option.

Aggravating factor: --show-config gives no hint

agent_spec  = my_agent.py:graph   [toml (config.toml)]   (env: LANGSTAGE_AGENT_SPEC …, toml: agent.spec)

It shows the raw relative value with no indication of the base directory it will resolve against, so a user debugging the 404 can't tell from --show-config that a global-vs-project distinction is in play. (Surfacing the resolved absolute agent path in --show-config would make this class of resolution surprise self-diagnosing.)

Severity

Minor — the workaround (use an absolute path or a module:attr spec in the global config) is easy once you know the rule; the cost is the silent, undocumented divergence from the project/env behavior that the docs present as equivalent.


Filed by the nightly dogfood routine.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingroutineFiled by the daily power-user dogfooding routine

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions