{"record":{"id":"2a465dd54115c4c3","repo":"HKUDS/Vibe-Trading","slug":"shoonya-login-failed-error-msg","errorCode":null,"errorMessage":"Shoonya login failed: {error_msg}","messagePattern":"Shoonya login failed: (.+?)","errorType":"exception","errorClass":"ShoonyaConfigError","httpStatus":null,"severity":"critical","filePath":"agent/src/trading/connectors/shoonya/sdk.py","lineNumber":590,"sourceCode":"                websocket=\"wss://api.shoonya.com/NorenWSTP/\",\n            )\n\n    api = ShoonyaApi()\n\n    totp = pyotp.TOTP(cfg.totp_secret).now()\n\n    ret = api.login(\n        userid=cfg.user_id,\n        password=cfg.password,\n        twoFA=totp,\n        vendor_code=cfg.vendor_code,\n        api_secret=cfg.api_secret,\n        imei=\"vibe-trading\",\n    )\n\n    if ret is None or ret.get(\"stat\") != \"Ok\":\n        error_msg = ret.get(\"emsg\", \"Login failed\") if ret else \"Login returned None\"\n        raise ShoonyaConfigError(f\"Shoonya login failed: {error_msg}\")\n\n    _api_cache[cfg.user_id] = api\n    return api\n\n\ndef _missing_fields(cfg: ShoonyaConfig) -> list[str]:\n    missing = []\n    for field in (\"user_id\", \"password\", \"vendor_code\", \"api_secret\", \"totp_secret\"):\n        if not getattr(cfg, field):\n            missing.append(field)\n    return missing\n\n\ndef _public_config(cfg: ShoonyaConfig) -> dict[str, Any]:\n    data = asdict(cfg)\n    for secret in (\"password\", \"api_secret\", \"totp_secret\"):\n        if data.get(secret):\n            data[secret] = \"***redacted***\"","sourceCodeStart":572,"sourceCodeEnd":608,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/trading/connectors/shoonya/sdk.py#L572-L608","documentation":"Raised by the Shoonya connector's _login helper when the NorenApi login call returns None or a payload whose 'stat' field is not 'Ok'. The upstream error message ('emsg') is embedded when present. It is wrapped as ShoonyaConfigError, meaning the connector treats failed credentials/connectivity at login as a configuration problem.","triggerScenarios":"Any call to get_account_snapshot, get_positions, get_open_orders, get_quote, or get_historical_bars on a cold (or invalidated) API cache triggers _login; if userid/password/vendor_token/api_secret/imei are wrong, TOTP is stale, or the Noren endpoint is unreachable, ret is None or stat != 'Ok'.","commonSituations":"Expired or changed Shoonya password, wrong vendor code/token/secret pair, missing TOTP factor, base URL pointing at the wrong environment (uat vs prod), or network/firewall blocking the broker's REST endpoint.","solutions":["Verify cfg.user_id, cfg.password, cfg.vendor_token, cfg.api_secret against the Shoonya/Noren developer portal and regenerate the vendor token if stale","Confirm the TOTP/2FA factor value is current and the imei string matches what is registered","Check the configured Norden base URL environment (UAT vs production) matches the credentials","If ret is None, test raw connectivity (curl the Shoonya endpoint) and inspect network/proxy settings","Catch ShoonyaConfigError at the call site and surface the embedded emsg for the broker-side reason"],"exampleFix":"# before\ncfg = ShoonyaConfig(user_id='u', password='wrong', ...)\napi = _login(cfg)  # raises 'Shoonya login failed: ...'\n\n# after\ncfg = ShoonyaConfig(user_id='u', password=os.environ['SHOONYA_PASSWORD'],\n                    vendor_token=os.environ['SHOONYA_VENDOR_TOKEN'],\n                    api_secret=os.environ['SHOONYA_API_SECRET'])","handlingStrategy":"try-catch","validationCode":"from agent.src.trading.connectors.shoonya.sdk import ShoonyaConfigError\n\ndef can_login(cfg) -> bool:\n    missing = _missing_fields(cfg)\n    if missing:\n        return False\n    try:\n        _login(cfg)\n        return True\n    except ShoonyaConfigError:\n        return False","typeGuard":"def is_shoonya_config_error(exc: BaseException) -> bool:\n    return isinstance(exc, ShoonyaConfigError) and exc.args[0].startswith('Shoonya login failed')","tryCatchPattern":"try:\n    snapshot = get_account_snapshot(cfg)\nexcept ShoonyaConfigError as exc:\n    if 'login failed' in str(exc):\n        logger.error('Shoonya auth rejected: %s', exc)\n        # re-read credentials / alert operator; do not retry in a tight loop\n    raise","preventionTips":["Store Shoonya credentials in env vars/secret manager, never hand-edited JSON","Run a login health-check at service startup (call one cheap endpoint) and fail fast","Refresh vendor tokens on a schedule before they expire"],"tags":["shoonya","login","credentials","broker-auth"],"backgroundTag":"broker-authentication-failed","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}