Para apagar mensagem retida MQTT você não precisa de uma ferramenta de administração do broker: basta publicar um payload de comprimento zero com a flag retain ativada no mesmo topic, e o broker descarta o valor armazenado. Este guia mostra essa técnica pela linha de comando, Python, JavaScript e um client no navegador, além de como encontrar e limpar em massa, com segurança, topics retidos obsoletos.
Por que as retained messages ficam presas
Uma retained message é a última mensagem que o broker armazena para um topic, para que qualquer novo assinante receba imediatamente o valor atual. É exatamente o que você quer para o status de um dispositivo, um documento de configuração ou a leitura mais recente de um sensor. Se o recurso é novidade para você, o guia de retained messages no MQTT explica a mecânica em detalhes.
O problema começa quando o valor armazenado deixa de ser verdadeiro. Situações típicas:
- Um dispositivo foi descomissionado, por exemplo um sensor retirado de uma estação de saneamento, mas seu último status
onlinecontinua aparecendo nos dashboards. - Um publish de teste com a flag retain, feito durante o desenvolvimento, agora polui uma árvore de topics de produção.
- Um topic de configuração foi renomeado, e o antigo ainda entrega configurações desatualizadas a novos clients.
- Um bug de firmware publicou JSON malformado com retain, e todo assinante que se reconecta trava ao processá-lo.
Como o broker reenvia o valor retido a cada nova assinatura, o problema nunca some sozinho. Você precisa substituir o valor armazenado ou removê-lo.
Como apagar uma mensagem retida no MQTT
A especificação MQTT define a regra em uma frase: um pacote PUBLISH com a flag RETAIN igual a 1 e payload de comprimento zero remove qualquer retained message daquele topic, e o broker não armazena uma nova. A mensagem vazia também é encaminhada aos assinantes atuais, então seus consumidores devem tolerar um payload vazio em vez de tentar interpretá-lo como JSON.
Alguns detalhes importam na prática:
- O topic precisa coincidir exatamente. As retained messages são armazenadas por nome de topic. Publicar uma mensagem retida vazia em
sensors/#não faz nada, porque wildcards não são permitidos em nomes de topic nos quais se publica. - O payload precisa estar realmente vazio. Um único espaço, um objeto JSON vazio
{}ou a stringnullsão payloads reais e serão retidos como o novo valor. - O QoS não importa muito. QoS 0 basta para uma limpeza, mas QoS 1 dá a você uma confirmação de que o broker a recebeu.
- Você precisa de permissão de publish naquele topic. Se suas credenciais não podem publicar ali, a exclusão é rejeitada ou descartada silenciosamente, dependendo do broker e da versão do protocolo.
Limpando retained messages com mosquitto_pub
As ferramentas de linha de comando do Mosquitto são a forma mais rápida de limpar um único topic. A flag -r ativa o retain e -n envia uma mensagem nula (de comprimento zero):
# Inspecione o que está retido atualmente no topic
mosquitto_sub -h broker.connectmq.com -p 1883 -i inspect-7f3a \
-t "connectmq/demo/7f3a/device42/status" -v -C 1 -W 5
# Apague a retained message
mosquitto_pub -h broker.connectmq.com -p 1883 -i cleanup-7f3a \
-t "connectmq/demo/7f3a/device42/status" -r -n
# Confirme: agora isto deve estourar o tempo sem imprimir nada
mosquitto_sub -h broker.connectmq.com -p 1883 -i verify-7f3a \
-t "connectmq/demo/7f3a/device42/status" -v -C 1 -W 5
A combinação -C 1 -W 5 diz ao mosquitto_sub para sair após uma mensagem ou cinco segundos, o que o transforma em uma sonda rápida de "há algo retido aqui?". Os exemplos usam o servidor MQTT público (gratuito, para testes e protótipos, não para dados de produção) com um prefixo único; substitua 7f3a pelo seu próprio ID aleatório.
Em um broker MQTT gerenciado privado, adicione credenciais e TLS:
mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_CLIENT_ID \
-t "plant1/line2/press/status" -r -n -q 1
Limpeza em massa com --remove-retained
O mosquitto_sub tem uma opção útil para subárvores inteiras. Com --remove-retained, cada retained message recebida é limpa com a publicação de uma mensagem retida vazia de volta naquele topic. Combine-a com --retained-only para que ela só toque nos valores retidos e ignore o tráfego ao vivo:
mosquitto_sub -h broker.connectmq.com -p 1883 -i purge-7f3a \
-t "connectmq/demo/7f3a/#" --retained-only --remove-retained -v -W 5
Rode primeiro o comando de inspeção, leia a lista com atenção e só então faça a limpeza. Um wildcard como # em uma árvore compartilhada pode apagar muito mais do que você pretendia.
Apagando uma mensagem retida com Python
Quando a limpeza faz parte de um script de descomissionamento, Python com paho-mqtt 2.x é conveniente. Passar payload=None produz um payload de comprimento zero:
import time
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
TOPIC = "connectmq/demo/7f3a/device42/status"
def on_connect(client, userdata, flags, reason_code, properties):
print("Connected:", reason_code)
info = client.publish(TOPIC, payload=None, qos=1, retain=True)
info.wait_for_publish(timeout=5)
print("Retained message cleared on", TOPIC)
client.disconnect()
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="cleanup-py-7f3a")
client.on_connect = on_connect
client.connect(BROKER, 1883, keepalive=30)
client.loop_forever()
Encontrando e limpando uma subárvore inteira
Esta versão assina com um wildcard, coleta todos os topics que entregam uma retained message durante uma janela curta e depois os limpa um a um. Ela limita a taxa de publicação, o que importa no servidor MQTT público, onde cada client está limitado a 10 mensagens por segundo.
import time
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
FILTER = "connectmq/demo/7f3a/#"
found = set()
def on_message(client, userdata, msg):
if msg.retain and msg.payload:
found.add(msg.topic)
def on_connect(client, userdata, flags, reason_code, properties):
client.subscribe(FILTER, qos=1)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="purge-py-7f3a")
client.on_connect = on_connect
client.on_message = on_message
client.connect(BROKER, 1883, keepalive=30)
client.loop_start()
time.sleep(3) # coleta as retained messages
print(f"Found {len(found)} retained topics")
for topic in sorted(found):
print("Clearing", topic)
client.publish(topic, payload=None, qos=1, retain=True).wait_for_publish(5)
time.sleep(0.15) # fica abaixo do limite de taxa por client
client.loop_stop()
client.disconnect()
Repare na verificação msg.retain. Os brokers ativam a flag retain nas mensagens entregues por causa de uma nova assinatura, então mensagens ao vivo que por acaso cheguem durante a janela não são confundidas com valores armazenados.
Apagando uma mensagem retida via JavaScript ou navegador
Com o MQTT.js vale a mesma regra: uma string vazia ou um buffer vazio com retain: true.
const mqtt = require("mqtt");
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
clientId: "cleanup-js-7f3a",
protocolVersion: 5,
});
client.on("connect", () => {
client.publish(
"connectmq/demo/7f3a/device42/status",
"",
{ qos: 1, retain: true },
(err) => {
console.log(err ? `Failed: ${err.message}` : "Retained message cleared");
client.end();
}
);
});
Se você prefere não escrever código algum, o client MQTT integrado ao navegador no portal ConnectMQ também funciona: conecte-se ao seu broker, informe o topic exato, deixe o campo de payload vazio, ative o retain e publique. O explorador de topics ao vivo é uma boa forma de ver quais topics ainda guardam um valor antes e depois da limpeza.
Solução de problemas quando uma mensagem retida não some
| Sintoma | Causa provável | Solução |
|---|---|---|
| O valor antigo reaparece segundos depois | Um dispositivo ou serviço ainda o publica com retain | Encontre o publicador, corrija sua flag retain e limpe de novo |
| Nada muda após o publish vazio | Topic diferente (maiúsculas, barra final, nível extra) | Copie o topic exato da saída de uma assinatura com -v |
| Assinantes travam após a limpeza | Os consumidores tentam interpretar o payload vazio | Ignore mensagens de comprimento zero no handler de mensagens |
| O valor é substituído, não removido | O payload era um espaço, {} ou null |
Use -n ou payload=None, não uma string de preenchimento |
| A limpeza funciona no teste, mas não em produção | As credenciais não têm permissão de publish naquele topic | Use a conta dona da árvore de topics |
A primeira linha é, de longe, a mais comum. Um valor retido que "volta" quase sempre está sendo republicado por alguma coisa, muitas vezes um script de bridge, um fluxo do Node-RED ou um dispositivo que publica seu status com retain a cada reconexão.
Evite dados retidos obsoletos desde o início
Apagar retained messages é uma tarefa de limpeza; projetá-las bem reduz a frequência com que você precisa fazer isso.
- Use o message expiry do MQTT 5. Defina um Message Expiry Interval nos publishes retidos para que o broker os descarte após um período. O guia do message expiry interval aborda valores sensatos.
- Combine status com Last Will. Um status
onlineretido deve vir acompanhado de uma mensagem de Last Willofflineretida, para que o valor armazenado continue correto quando um dispositivo cair. - Mantenha os topics retidos restritos. Retenha estado, não fluxos. Telemetria de alta frequência raramente precisa de retain, e menos topics retidos significam menos coisa para limpar.
- Não trate retained messages como banco de dados. Elas guardam o valor mais recente por conveniência, não um histórico durável. Armazene o que você precisa manter em um banco de dados de verdade, alimentado por um assinante.
- Documente quais topics são retidos. Uma lista curta nas suas convenções de nomenclatura de topics evita surpresas quando alguém adiciona uma assinatura com wildcard.
O comportamento completo da flag retain está especificado no padrão OASIS MQTT 5.0, e o tutorial de MQTT, mais amplo, coloca as retained messages em contexto com QoS, sessões e wills.
Gerencie topics retidos no ConnectMQ
O ConnectMQ suporta retained messages tanto no MQTT 3.1.1 quanto no 5.0, então todas as técnicas acima funcionam sem alterações. Se você está indo além dos experimentos, um broker MQTT gerenciado oferece credenciais privadas, TLS na porta 8883 e um portal com client integrado ao navegador e explorador de topics ao vivo para esse tipo de manutenção. Você pode ler mais sobre como rodar um broker MQTT na nuvem sem operar servidores você mesmo.
Crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito, e em poucos minutos você terá username, password e client ID.
Perguntas frequentes
Como apago uma mensagem retida no MQTT?
Publique uma mensagem com payload de comprimento zero e a flag retain ativada exatamente no mesmo topic. Com o Mosquitto, isso é mosquitto_pub -t <topic> -r -n, e o broker deixa de armazenar um valor retido para aquele topic.
Posso apagar todas as mensagens retidas com um wildcard?
Não com um único publish, porque wildcards não são permitidos em nomes de topic. Assine com um filtro wildcard para descobrir os topics retidos e depois limpe cada um, ou use mosquitto_sub --retained-only --remove-retained.
Enviar um objeto JSON vazio apaga a mensagem retida?
Não. {} é um payload de dois bytes, então o broker o retém como o novo valor. Só um payload realmente vazio remove a retained message.
Os assinantes recebem a mensagem vazia?
Sim, os assinantes atuais recebem a mensagem de comprimento zero como qualquer outro publish. Garanta que seus consumidores ignorem payloads vazios em vez de falhar ao interpretá-los.



