{"record":{"id":"24a8458d7cc9300e","repo":"HKUDS/Vibe-Trading","slug":"slack-target-user-handle-is-empty","errorCode":null,"errorMessage":"Slack target user handle is empty","messagePattern":"Slack target user handle is empty","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"agent/src/channels/slack.py","lineNumber":264,"sourceCode":"            for channel in response.get(\"channels\", []):\n                if self._normalize_target_name(str(channel.get(\"name\") or \"\")) == normalized:\n                    channel_id = str(channel.get(\"id\") or \"\")\n                    if channel_id:\n                        self._target_cache[cache_key] = channel_id\n                        return channel_id\n            cursor = ((response.get(\"response_metadata\") or {}).get(\"next_cursor\") or \"\").strip()\n            if not cursor:\n                break\n\n        raise ValueError(\n            f\"Slack channel '{name}' was not found. Use a joined channel name like \"\n            f\"'#general' or a concrete channel ID.\"\n        )\n\n    async def _resolve_user_handle(self, handle: str) -> str:\n        normalized = self._normalize_target_name(handle)\n        if not normalized:\n            raise ValueError(\"Slack target user handle is empty\")\n\n        cache_key = f\"user:{normalized}\"\n        if cache_key in self._target_cache:\n            return self._target_cache[cache_key]\n\n        cursor: str | None = None\n        while True:\n            response = await self._web_client.users_list(limit=200, cursor=cursor)\n            for member in response.get(\"members\", []):\n                if self._member_matches_handle(member, normalized):\n                    user_id = str(member.get(\"id\") or \"\")\n                    if not user_id:\n                        continue\n                    dm_id = await self._open_dm_for_user(user_id)\n                    self._target_cache[cache_key] = dm_id\n                    return dm_id\n            cursor = ((response.get(\"response_metadata\") or {}).get(\"next_cursor\") or \"\").strip()\n            if not cursor:","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/channels/slack.py#L246-L282","documentation":"User-handle resolution failed because the handle, after normalization (strip, remove leading @, lowercase), is empty.","triggerScenarios":"Sending to a target of '@', '', or whitespace interpreted as a user handle.","commonSituations":"Placeholder target '@username' not filled in, or trailing/odd user input reduced to just '@'.","solutions":["Pass a real handle like '@alice' or a user ID (U...)","Validate targets before dispatch","Fix any templating producing empty handles"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if not handle.strip().lstrip('@'):\n    raise ValueError('empty Slack user handle')","typeGuard":"def is_valid_slack_handle(h: str) -> bool:\n    return bool(h and h.strip().lstrip('@'))","tryCatchPattern":"except ValueError as e:\n    if 'handle is empty' in str(e):\n        reject_user_input('invalid user target')","preventionTips":["Validate DM targets before send","Check template interpolation for @handles"],"tags":["slack","target","validation"],"backgroundTag":"empty-target-name","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}