I’m trying to build a workflow to bulk mark unread Gmail emails as read, but I’m hitting a persistent error on the step that modifies the email.
Goal:
Find unread emails in the inbox and mark them as read.
Setup:
-
N8N Version: 1.86.1 (Cloud)
-
Gmail Node Version: 2.1 (Latest)
-
Node 1: Gmail Node
-
Operation: Get Many
-
Resource: Message
-
Filters: Read Status: Unread emails only AND Label IDs Contains INBOX
-
Limit: Set to a batch size (e.g., 150)
-
-
Node 2: Gmail Node
-
Operation: Tried both Mark as Read and Remove Label
-
Resource: Message
-
Message ID: Using Expression ={{ $json.id }} (or variants)
-
Label Names or IDs (for Remove Label): UNREAD
-
Problem:
Node 1 successfully retrieves the unread emails. The output table clearly shows the message id (e.g., 196255541640182a).
However, Node 2 consistently fails with the error:
ERROR: Bad request - please check your parameters (item 0)
Invalid id value
(From Gmail: { “error”: { “code”: 400, “message”: “Invalid id value”, “errors”: [ { “message”: “Invalid id value”, “domain”: “global”, “reason”: “invalidArgument” } ], “status”: “INVALID_ARGUMENT” } })
This error occurs when the Message ID field uses the expression ={{ $json.id }}. The expression preview shows the correct ID value before failing.
Troubleshooting Steps Taken:
-
Verified Node 1 output contains the correct id field.
-
Tried Node 2 Operation Mark as Read with Message ID ={{ $json.id }} → Fails.
-
Tried Node 2 Operation Remove Label with Label UNREAD and Message ID ={{ $json.id }} → Fails.
-
Tried adding .toString() to the expression: ={{ $json.id.toString() }} → Fails.
-
Tried using the threadId just in case: ={{ $json.threadId }} / ={{ $json.threadId.toString() }} → Fails.
-
Tried running the full workflow vs. “Test step” → Fails both ways.
-
Deleted and recreated the Gmail OAuth2 credentials, ensuring all permissions were granted → Fails.
-
Crucially: If I switch Node 2’s “Message ID” field to Fixed mode and paste the exact same ID (copied from Node 1’s output) directly into the field, the “Remove Label” operation SUCCEEDS for that specific message.
Conclusion:
Since the operation works perfectly with a hardcoded ID but fails consistently when using the {{ $json.id }} expression (even though the value appears correct), it seems the issue lies specifically with how the expression-derived value is being passed to the API by this version of the node.
I found this related thread (https://community.n8n.io/t/gmail-ai-agent-read-messages-and-mark-them-as-read/73926/2) which confirms the “Remove Label” UNREAD method with ={{ $json.id }} should be the correct approach.
Is this a known issue with N8N v1.86.1 / Gmail Node v2.1, or is there anything else I might be missing? Any insights would be greatly appreciated!
Thanks!