AlexxIT/go2rtc · critical

${loginResp.ErrorMsg}

Error message

${loginResp.ErrorMsg}

What it means

PasswordLogin returns this error when the Tuya password-login API responds with Success=false; loginResp.ErrorMsg (this endpoint's error field name) is passed to errors.New. The login failed, so MQTT URLs and expiry are never set and the client is unusable. The server's message is propagated unchanged.

Solutions

  1. Read ErrorMsg for the exact login failure reason and correct credentials first.
  2. Re-run initToken to fetch a fresh public key before encrypting the password.
  3. Verify account status (locked/2FA/captcha) in the Tuya app or developer console.
  4. Confirm the region/endpoint matches the account's region.
  5. Retry with backoff only for transient server messages.

Example fix

// before
if err := client.Login(); err != nil {
    return err // raw ErrorMsg, no context
}
// after
if err := client.Login(); err != nil {
    return fmt.Errorf("tuya password login failed: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if client.password == "" { return errors.New("password required") }; ensure initToken() succeeded and key is fresh before Login()

Try / catch

if err := client.Login(); err != nil {
    if isAuthRelated(err.Error()) {
        return fmt.Errorf("tuya login rejected: %w", err) // fix creds, no retry
    }
    return fmt.Errorf("tuya login: %w", err)
}

Prevention

When it happens

Trigger: Calling the password-login flow when the response has Success=false — wrong credentials, account locked, or cloud-side rejection of the login request.

Common situations: Invalid/rotated Tuya password; account requires captcha or 2FA; wrong region; encrypted password built from a stale public key; Tuya cloud maintenance.

Related errors


AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07). Data as JSON: /api/errors/97e51ad3b1532623. Report an issue: GitHub.

Appendix: source

Thrown at pkg/tuya/smart_api.go:468

		loginUrl = fmt.Sprintf("https://%s/api/private/email/login", c.baseUrl)
		loginReq.Email = c.email
	} else {
		loginUrl = fmt.Sprintf("https://%s/api/private/phone/login", c.baseUrl)
		loginReq.Mobile = c.email
	}

	body, err = c.request("POST", loginUrl, loginReq)
	if err != nil {
		return err
	}

	var loginResp *PasswordLoginResponse
	if err := json.Unmarshal(body, &loginResp); err != nil {
		return err
	}

	if !loginResp.Success {
		return errors.New(loginResp.ErrorMsg)
	}

	c.mqttsUrl = fmt.Sprintf("ssl://%s:%d", loginResp.Result.Domain.MobileMqttsUrl, loginResp.Result.Domain.MqttsPort)
	c.expireTime = time.Now().Unix() + 2*24*60*60 // 2 days in seconds

	return nil
}

func (c *TuyaSmartApiClient) loadWebrtcConfig() (*WebRTCConfig, error) {
	url := fmt.Sprintf("https://%s/api/jarvis/config", c.baseUrl)

	data := SmartApiWebRTCConfigRequest{
		DevId:         c.deviceId,
		ClientTraceId: fmt.Sprintf("%x", rand.Int63()),
	}

	body, err := c.request("POST", url, data)
	if err != nil {

View on GitHub (pinned to c245815e75)