{"record":{"id":"6d7914efd42aec56","repo":"calesthio/OpenMontage","slug":"cost-entry-entry-id-r-not-found","errorCode":null,"errorMessage":"Cost entry {entry_id!r} not found","messagePattern":"Cost entry (.+?) not found","errorType":"exception","errorClass":"KeyError","httpStatus":null,"severity":"error","filePath":"tools/cost_tracker.py","lineNumber":515,"sourceCode":"        }\n        self.cost_log_path.parent.mkdir(parents=True, exist_ok=True)\n        with open(self.cost_log_path, \"w\") as f:\n            json.dump(data, f, indent=2)\n\n    def _load(self) -> None:\n        with open(self.cost_log_path) as f:  # type: ignore[arg-type]\n            data = json.load(f)\n        self.entries = data.get(\"entries\", [])\n        self.budget_total_usd = data.get(\"budget_total_usd\", self.budget_total_usd)\n        self._approved_tools = set(data.get(\"approved_tools\", []))\n\n    # ---- Helpers ----\n\n    def _find(self, entry_id: str) -> dict[str, Any]:\n        for entry in self.entries:\n            if entry[\"id\"] == entry_id:\n                return entry\n        raise KeyError(f\"Cost entry {entry_id!r} not found\")\n\n    @staticmethod\n    def _new_id() -> str:\n        return uuid.uuid4().hex[:12]\n\n    @staticmethod\n    def _now() -> str:\n        return datetime.now(timezone.utc).isoformat()\n","sourceCodeStart":497,"sourceCodeEnd":524,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/cost_tracker.py#L497-L524","documentation":"KeyError raised by CostTracker._find when no entry in self.entries has id == entry_id. All entry-scoped operations (reserve, settle, cancel, annotate) locate entries through _find, so any stale or mistyped id surfaces here. It signals the entry was never created, was removed, or belongs to a different cost log.","triggerScenarios":"Calling reserve/settle/cancel with an id that was never logged via add_estimate, an id from a previous session's cost log file, or a typo/truncated id (ids are 12-hex-char strings from _new_id).","commonSituations":"Persisting entry ids across process restarts where cost_log_path points to a rotated/different file, concurrent writers overwriting the log, or passing the entry dict instead of its id string.","solutions":["Create the entry first: capture the id returned by add_estimate/log and pass that exact string to reserve/settle.","If the id came from an earlier run, confirm you loaded the same cost_log_path (the log is reloaded on startup; entries must be in it).","Guard lookups by checking `any(e['id'] == entry_id for e in tracker.entries)` before acting, or catch KeyError and re-create the entry.","If multiple processes share a log, serialize writes so entries are not lost."],"exampleFix":"# before\ntracker.settle(\"abc123deadbee\")  # typo -> KeyError('Cost entry ... not found')\n\n# after\nentry_id = tracker.add_estimate(tool=\"seedance_video\", estimated_usd=0.5)\nassert any(e[\"id\"] == entry_id for e in tracker.entries)\ntracker.settle(entry_id)","handlingStrategy":"validation","validationCode":"def entry_exists(tracker, entry_id: str) -> bool:\n    return any(e[\"id\"] == entry_id for e in tracker.entries)","typeGuard":"def is_entry_id(value: str) -> bool:\n    return isinstance(value, str) and len(value) == 12 and all(c in \"0123456789abcdef\" for c in value)","tryCatchPattern":"try:\n    tracker.settle(entry_id)\nexcept KeyError as e:\n    if \"not found\" in str(e):\n        entry_id = tracker.add_estimate(tool=tool, estimated_usd=est)  # recreate\n        tracker.settle(entry_id)\n    else:\n        raise","preventionTips":["Always use the id returned by add_estimate; never hand-build ids.","Ensure the same cost_log_path is used across processes that share entries.","Validate entry existence before reserve/settle in long-lived pipelines."],"tags":["cost-tracking","lookup","state","keyerror"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}