{"record":{"id":"440ed300f92a5772","repo":"sipeed/picoclaw","slug":"invalid-chat-id-q-w","errorCode":null,"errorMessage":"invalid chat id %q: %w","messagePattern":"invalid chat id %q: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/whatsapp_native/whatsapp_native.go","lineNumber":436,"sourceCode":"\t}\n\n\tc.mu.Lock()\n\tclient := c.client\n\tc.mu.Unlock()\n\n\tif client == nil || !client.IsConnected() {\n\t\treturn nil, fmt.Errorf(\"whatsapp connection not established: %w\", channels.ErrTemporary)\n\t}\n\n\t// Detect unpaired state: the client is connected (to WhatsApp servers)\n\t// but has not completed QR-login yet, so sending would fail.\n\tif client.Store.ID == nil {\n\t\treturn nil, fmt.Errorf(\"whatsapp not yet paired (QR login pending): %w\", channels.ErrTemporary)\n\t}\n\n\tto, err := parseJID(msg.ChatID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid chat id %q: %w\", msg.ChatID, err)\n\t}\n\n\twaMsg := &waE2E.Message{\n\t\tConversation: proto.String(msg.Content),\n\t}\n\n\tif _, err = client.SendMessage(ctx, to, waMsg); err != nil {\n\t\treturn nil, fmt.Errorf(\"whatsapp send: %w\", channels.ErrTemporary)\n\t}\n\treturn nil, nil\n}\n\n// parseJID converts a chat ID (phone number or JID string) to types.JID.\nfunc parseJID(s string) (types.JID, error) {\n\ts = strings.TrimSpace(s)\n\tif s == \"\" {\n\t\treturn types.JID{}, fmt.Errorf(\"empty chat id\")\n\t}","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/whatsapp_native/whatsapp_native.go#L418-L454","documentation":"Returned by WhatsAppNativeChannel.Send when parseJID cannot convert msg.ChatID into a whatsmeow types.JID: the value contains '@' but fails types.ParseJID (malformed JID like 'abc@', '@s.whatsapp.net', or bad server suffix). The original error is wrapped with %w and the offending chat id is quoted. Note this error carries no ErrTemporary/ErrSendFailed sentinel, so the manager treats it as unknown and retries with backoff even though it is permanent.","triggerScenarios":"Send() with msg.ChatID containing '@' but not matching 'user@server' structure; trailing/leading '@'; unsupported server domain; NUL/control characters in the id.","commonSituations":"Upstream systems forwarding group ids, wa.me links pasted verbatim, or phone numbers mangled with an email-like suffix ('+15551234567@') produce this. Fixing the source of chat ids is the durable remedy.","solutions":["Log/inspect the quoted chat id from the error string and correct the producer that generated it.","Use a bare international phone number ('15551234567') or a full JID ('15551234567@s.whatsapp.net').","If routing from another channel, sanitize chat ids before they reach the WhatsApp channel.","Consider wrapping with channels.ErrSendFailed upstream so the manager dead-letters instead of endlessly retrying a permanent id problem."],"exampleFix":"// before\nto, err := parseJID(msg.ChatID)\nif err != nil {\n    return nil, fmt.Errorf(\"invalid chat id %q: %w\", msg.ChatID, err)\n}\n// after\nto, err := parseJID(msg.ChatID)\nif err != nil {\n    return nil, fmt.Errorf(\"invalid chat id %q: %w\", msg.ChatID, channels.ErrSendFailed)\n}","handlingStrategy":"validation","validationCode":"func validWhatsAppID(id string) error {\n    id = strings.TrimSpace(id)\n    if id == \"\" { return errors.New(\"empty chat id\") }\n    if strings.Contains(id, \"@\") {\n        parts := strings.SplitN(id, \"@\", 2)\n        if parts[0] == \"\" || parts[1] == \"\" { return fmt.Errorf(\"malformed jid %q\", id) }\n    } else if !isDigits(id) {\n        return fmt.Errorf(\"expected digits-only phone, got %q\", id)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := ch.Send(ctx, msg); err != nil {\n    var jidErr *waErrJID // or match on message text if typed error unavailable\n    if strings.Contains(err.Error(), \"invalid chat id\") {\n        // permanent data problem: fix source, do not blind-retry\n    }\n}","preventionTips":["Normalize chat ids to bare international phone numbers at ingestion.","Validate ids before they enter outbound queues.","Remember this error has no sentinel, so manager retries it as unknown - dead-letter manually."],"tags":["whatsapp","jid","validation","chat-id"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}