HKUDS/Vibe-Trading · error · StaleGoalError
expected_goal_id does not match target goal
Error message
expected_goal_id does not match target goal
What it means
Thrown by GoalStore._require_mutable_goal when the expected_goal_id passed for optimistic concurrency does not equal the target goal_id. The store requires callers to prove they are acting on the goal version they last saw; a mismatch means the client's reference is stale or simply wrong.
Source
Thrown at agent/src/goal/store.py:832
now,
goal_id,
session_id,
),
)
updated = self.get_goal(goal_id)
if updated is None:
raise RuntimeError("usage-updated goal could not be reloaded")
return updated
def _require_mutable_goal(
self,
session_id: str,
goal_id: str,
expected_goal_id: str,
) -> GoalRecord:
if expected_goal_id != goal_id:
raise StaleGoalError("expected_goal_id does not match target goal")
session_id = normalize_required_text(session_id, "session_id")
goal_id = normalize_required_text(goal_id, "goal_id")
goal = self.get_goal(goal_id)
if goal is None or goal.session_id != session_id:
raise StaleGoalError("goal is not available for this session")
if goal.status not in _CURRENT_STATUSES:
raise StaleGoalError(f"goal status {goal.status.value!r} is not mutable")
current = self.get_current_goal(session_id)
if current is None or current.goal_id != goal_id:
raise StaleGoalError("goal is not current for this session")
return goal
@staticmethod
def _verification_status(evidence: EvidenceInput) -> str:
"""Return whether evidence has a traceable local artifact/run source."""
if evidence.artifact_path:
try:
artifact = safe_document_path(evidence.artifact_path)View on GitHub (pinned to 80ffdda44c)
Solutions
- Re-fetch the session's current goal (get_current_goal) and retry with its goal_id as expected_goal_id
- If racing another writer, resolve the conflict first — decide whether the stale operation should be dropped or re-applied to the new goal
- Add a pre-call check expected_goal_id == goal_id to fail fast in client code
Example fix
// before
store.update_goal(session_id, goal_id=gid, expected_goal_id=old_gid, ...)
// after
current = store.get_current_goal(session_id)
if current is None or current.goal_id != gid:
gid = current.goal_id
store.update_goal(session_id, goal_id=gid, expected_goal_id=gid, ...) Defensive patterns
Strategy: validation
Validate before calling
goal = store.get_goal(goal_id)
if goal is None or goal.goal_id != expected_goal_id:
current = store.get_current_goal(session_id)
goal_id = expected_goal_id = current.goal_id if current else None Type guard
def is_fresh_goal_ref(store, session_id: str, goal_id: str, expected: str) -> bool:
return expected == goal_id and (store.get_current_goal(session_id) or object()).goal_id == goal_id Try / catch
except StaleGoalError: refresh current goal from store and retry once; escalate if it persists
Prevention
- Always derive goal_id from get_current_goal rather than caching
- Pass expected_goal_id == goal_id by construction
- Refresh goal refs after any operation that can create goals
When it happens
Trigger: Calling update_goal, append_evidence, update_status, or account_usage with expected_goal_id != goal_id, e.g. after the session's current goal has switched and the client reuses an old expected id.
Common situations: Agent loop holds a cached goal id after a new goal was created; concurrent writers racing on the same session; copy-paste of ids between sessions.
Related errors
- goal is not current for this session
- {field_name} cannot be empty
- at least one goal criterion is required
- goal is not available for this session
- goal status {goal.status.value!r} is not mutable
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/0006ed0b3277beae.
Report an issue: GitHub.