Skip to content

[Bug]: google-workspace Gmail wrapper loses To/Subject due to case-sensitive header handling #34806

@jerome-benoit

Description

@jerome-benoit

Bug Description

The google-workspace skill's Gmail wrapper writes MIME headers with lowercase names (to, subject, cc, from) but later reads Gmail API headers with exact canonical names (To, Subject, From).

As a result, google_api.py gmail get MESSAGE_ID can report an empty to and subject for messages sent through google_api.py gmail send, even though the raw Gmail API payload contains those headers in lowercase.

Steps to Reproduce

  1. Use the installed google-workspace skill on Hermes v0.15.1.
  2. Send a Gmail message through the wrapper:
GAPI="python ${HERMES_HOME:-$HOME/.hermes}/skills/productivity/google-workspace/scripts/google_api.py"
$GAPI gmail send --to recipient@example.com --subject "Header test" --body "body"
  1. Read the returned message ID through the same wrapper:
$GAPI gmail get MESSAGE_ID
  1. Compare with the raw Gmail API payload or with a patched _headers_dict() that normalizes header names case-insensitively.

Expected Behavior

gmail get should return the sent message metadata with populated fields:

{
  "to": "recipient@example.com",
  "subject": "Header test"
}

gmail send / gmail reply should also emit conventional RFC header names (To, Subject, Cc, From) instead of lowercase variants.

Actual Behavior

The wrapper can return:

{
  "to": "",
  "subject": ""
}

The message is not necessarily missing those headers. The raw Gmail API payload contains them as lowercase to / subject, while _headers_dict() stores and reads header names case-sensitively.

Affected Component

  • Skills (skill loading, skill hub, skill guard)

Messaging Platform (if gateway-related)

  • N/A (CLI only)

Debug Report

N/A. This is a small code-level bug in skills/productivity/google-workspace/scripts/google_api.py, reproduced directly with the skill wrapper and confirmed by inspecting the Gmail API payload.

Operating System

Fedora Linux 43 (Sway)

Python Version

Hermes runtime: Python 3.12.13
System Python: Python 3.14.5

Hermes Version

Hermes Agent v0.15.1 (2026.5.29)

Additional Logs / Traceback (optional)

Installed skill markers on v0.15.1:

~/.hermes/skills/productivity/google-workspace/scripts/google_api.py
message["to"]      present
message["To"]      absent
message["subject"] present
message["Subject"] absent

Duplicate search performed before filing:

gh search issues --repo NousResearch/hermes-agent 'google workspace gmail headers lowercase' => []
gh search issues --repo NousResearch/hermes-agent 'gmail get empty subject' => []
gh search prs    --repo NousResearch/hermes-agent 'google workspace gmail headers' => []

Root Cause Analysis (optional)

gmail_send() and gmail_reply() build MIME messages with lowercase header names:

message["to"] = args.to
message["subject"] = args.subject
message["cc"] = args.cc
message["from"] = args.from_header

gmail_get() then uses _headers_dict() and exact canonical lookups:

def _headers_dict(msg: dict) -> dict[str, str]:
    return {h["name"]: h["value"] for h in msg.get("payload", {}).get("headers", [])}

headers.get("To", "")
headers.get("Subject", "")

Header field names are case-insensitive by RFC, but this dict access is case-sensitive.

Proposed Fix (optional)

  1. Emit canonical MIME header names in gmail_send() and gmail_reply():
message["To"] = ...
message["Subject"] = ...
message["Cc"] = ...
message["From"] = ...
  1. Make _headers_dict() robust by preserving original keys and also adding canonical aliases for common headers case-insensitively (from, to, cc, subject, date, message-id).

  2. Add regression tests covering:

    • _headers_dict() normalizes lowercase Gmail API header names.
    • gmail_send() builds raw MIME messages with canonical To / Subject header names.

Local validation with this fix:

python -m pytest tests/skills/test_google_workspace_api.py -q -o 'addopts='
11 passed in 0.29s

python -m pytest tests/skills/test_google_workspace_api.py tests/skills/test_google_oauth_setup.py -q -o 'addopts='
26 passed in 0.47s
  • I'd like to fix this myself and submit a PR

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low — cosmetic, nice to havetool/skillsSkills system (list, view, manage)type/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions