Skip to content

Validate feed keys in MQTT client to prevent malformed topic subscriptions#170

Merged
tyeth merged 12 commits intomasterfrom
copilot/fix-empty-feed-id-subscription
Feb 19, 2026
Merged

Validate feed keys in MQTT client to prevent malformed topic subscriptions#170
tyeth merged 12 commits intomasterfrom
copilot/fix-empty-feed-id-subscription

Conversation

Copy link
Contributor

Copilot AI commented Feb 12, 2026

MQTT client methods accepted empty or invalid feed keys, resulting in malformed topic subscriptions like tyeth/feeds/ (trailing slash). This affected 9 production users.

Changes

  • Added validate_feed_key() function enforcing Adafruit IO rules:

    • Max 128 characters
    • Pattern: ^[a-zA-Z0-9-]+((\/|\.)[a-zA-Z0-9-]+)?$
    • Raises TypeError for invalid patterns, ValueError for length violations
  • Validation applied to: subscribe(), publish(), unsubscribe(), receive()

  • Renamed parameter: feed_idfeed_key (clarifies string key vs numeric ID)

  • Added tests covering valid keys, empty strings, invalid characters, and invalid patterns

Example

from Adafruit_IO import MQTTClient

client = MQTTClient('username', 'key')

# Previously allowed, now raises TypeError
client.subscribe('')  # TypeError: Feed key must contain English letters, numbers, dash...

# Valid keys continue to work
client.subscribe('temperature')     # ✓
client.subscribe('group.feed')      # ✓
client.subscribe('group/feed')      # ✓

Reference: https://learn.adafruit.com/naming-things-in-adafruit-io/the-two-feed-identifiers

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • io.adafruit.com
    • Triggering command: /usr/bin/python python -m unittest test_mqtt_client.TestFeedKeyValidation test_mqtt_client.TestMQTTClientFeedKeyValidation test_model test_errors -v (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>It's technically possible to subscribe to /<user>/feeds/, but technically shouldn't be...</issue_title>
<issue_description>

(res, mid) = self._client.subscribe('{0}/feeds/{1}'.format(feed_user, feed_id, qos=qos))

If the user passes in an empty string for feed id, then this situation could arise. As of today, there are 9 users in production with such an MQTT topic subscription (ending in trailing slash).

>>> '{0}/feeds/{1}'.format('tyeth', None, 1)
'tyeth/feeds/None'
>>> '{0}/feeds/{1}'.format('tyeth', '', 1)
'tyeth/feeds/'
>>> '{0}/feeds/{1}'.format('tyeth')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
IndexError: Replacement index 1 out of range for positional args tuple
>>> '{0}/feeds/{1}'.format('tyeth',,1)
  File "<console>", line 1
    '{0}/feeds/{1}'.format('tyeth',,1)
                                   ^
SyntaxError: invalid syntax
>>> '{0}/feeds/{1}'.format('tyeth',a,1)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'a' is not defined

We should verify the feed id is not none or empty.
Maybe validate like https://github.com/adafruit/Adafruit_CircuitPython_AdafruitIO/blob/7246c228df891671e76d44256457f3753fac463e/adafruit_io/adafruit_io.py#L45</issue_description>

<agent_instructions>Implement the requested changes in the issue description and comments. Add appropriate tests for the mqtt client, so we can ensure it fails with empty feed keys, and passes otherwise.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@tyeth Body of feed name validation method: ``` def validate_feed_key(feed_key: str): """Validates a provided feed key against Adafruit IO's system rules. https://learn.adafruit.com/naming-things-in-adafruit-io/the-two-feed-identifiers """ if len(feed_key) > 128: # validate feed key length raise ValueError("Feed key must be less than 128 characters.") if not bool( re.match(r"^[a-zA-Z0-9-]+((\/|\.)[a-zA-Z0-9-]+)?$", feed_key) ): # validate key naming scheme raise TypeError( "Feed key must contain English letters, numbers, dash, and a period or a forward slash." ) ```

Note that we should rename arg to feed_key instead of feed_id in the calling method as it is the feed key not numeric id</comment_new>


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI changed the title [WIP] Fix subscription issue for empty feed ID Validate feed keys in MQTT client to prevent malformed topic subscriptions Feb 12, 2026
Copilot AI requested a review from tyeth February 12, 2026 18:21
@tyeth tyeth marked this pull request as ready for review February 12, 2026 20:07
@tyeth tyeth force-pushed the copilot/fix-empty-feed-id-subscription branch from 2c5b07d to ee4ea8f Compare February 12, 2026 20:10
Copy link
Member

@tyeth tyeth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, fixed temp_humidity example where humidity was using incorrect argument number in format string (ruff removed argument instead of fixing arg number - fixed)

@tyeth tyeth requested a review from brentru February 12, 2026 20:16
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because some function arguments have been renamed for clarity, technically a breaking change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally this needs to merge in before #171

import platform
import pkg_resources
try:
from importlib.metadata import version as package_version
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably add the <3.8 fallback, not sure our min version policy, but we say 3.6+ for now, so update like in #162 or retest with 3.6 + 3.7 then maybe safe to not need it.

try:
    from importlib.metadata import version as pkg_version  # Python 3.8+
except ImportError:
    try:
        from importlib_metadata import version as pkg_version  # Backport for <3.8
    except ImportError:
        pkg_version = None
        import pkg_blah

@tyeth
Copy link
Member

tyeth commented Feb 13, 2026

It might make sense to prepopulate the feed key to the subscribe mqtt function, to be "#" and update the regex, as technically wildcards can be supported (and possible expected default behaviour without supplying a feed key).

-EDIT- The docstrings suggest only a single feed should be monitored with the subscribe method, so no action needed.

Copy link
Member

@brentru brentru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tyeth
Copy link
Member

tyeth commented Feb 18, 2026

@brentru resolved / responded. Think this is fine now, and will be released through the airQ PR #172 so can be closed if approved.

@tyeth tyeth merged commit 94f9779 into master Feb 19, 2026
@tyeth tyeth deleted the copilot/fix-empty-feed-id-subscription branch February 19, 2026 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

It's possible to subscribe to /<user>/feeds/, but technically shouldn't be...

3 participants

Comments