# Mettre à jour une intégration externe échoue toujours : la nouvelle version s'affiche mais l'ancienne image continue de tourner (5.1.0)

**URL:** <https://community.gladysassistant.com/t/mettre-a-jour-une-integration-externe-echoue-toujours-la-nouvelle-version-saffiche-mais-lancienne-image-continue-de-tourner-5-1-0/10890>\
**Category:** Configuration\
**Created:** [Septembre 21, 2026, 8:26 UTC](https://community.gladysassistant.com/t/mettre-a-jour-une-integration-externe-echoue-toujours-la-nouvelle-version-saffiche-mais-lancienne-image-continue-de-tourner-5-1-0/10890 "2026-09-21T20:26:03Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![prohand](https://gladys-assistant-community.b-cdn.net/user_avatar/community.gladysassistant.com/prohand/32/15713_2.png) [@prohand](https://community.gladysassistant.com/u/prohand)\
**Post date:** [Septembre 21, 2026, 8:26 UTC](https://community.gladysassistant.com/t/mettre-a-jour-une-integration-externe-echoue-toujours-la-nouvelle-version-saffiche-mais-lancienne-image-continue-de-tourner-5-1-0/10890/1 "2026-09-21T20:26:03Z")

</div>

Bonjour,

Je suis tombé sur un bug qui touche **toutes** les intégrations externes, pas seulement la mienne : le bouton **Mettre à jour** d’une intégration externe échoue systématiquement, et surtout il échoue _à moitié, en silence_.

## Le symptôme

Après le clic sur « Mettre à jour », Gladys affiche bien la nouvelle version comme installée. Mais le conteneur, lui, n’a jamais été recréé :

```auto
$ docker inspect --format '{{.Config.Image}}' gladys-ext-<mon-intégration>

```

`ghcr.io/<...>:1.0.7 <-- l'ANCIENNE image, alors que l'UI affiche 2.0.0`

Autrement dit : **l’ancien code tourne sous le nouveau manifest**. Si la nouvelle version déclare des fonctionnalités que l’ancienne n’implémente pas (widgets, déclencheurs de scène…), le cœur les demande à un conteneur qui ne sait pas y répondre.

Dans les logs, au moment du clic :

```auto
<warn> errorMiddleware.js:83 TypeError: Cannot assign to read only property 'start' of object '#<Object>'

```

` at Object.assign (<anonymous>)`

` at Store.setState (/src/server/lib/state/Store.js:19:12)`

` at StateManager.setState (/src/server/lib/state/index.js:17:38)`

` at ExternalIntegration.registerProxyService (/src/server/lib/external-integration/externalIntegration.registerProxyService.js:238:21)`

` at ExternalIntegration.update (/src/server/lib/external-integration/externalIntegration.update.js:233:8)`

` at update (/src/server/api/controllers/externalIntegration.controller.js:306:25)`

## La cause

`registerProxyService()` construit le proxy avec `Object.freeze({ start, stop, device, ... })`, et `StateManager.setState()` **fusionne** dans le `Store` existant (`Object.assign`) au lieu de le remplacer.

- 1er enregistrement : l’état du `Store` est encore `null`, l’objet est affecté → OK.
- 2e enregistrement : `Object.assign(proxyGelé, nouveauProxy)` → exception sur sa première clé, `start`.

Or `init.js` enregistre **toutes** les intégrations externes installées au démarrage de Gladys. Donc au moment où on clique sur « Mettre à jour », le `Store` existe toujours, et la mise à jour ne peut jamais aboutir. À noter que `uninstall()` fait bien le ménage (`deleteState` sur les deux clés) — c’est pour ça que désinstaller/réinstaller fonctionne, alors que mettre à jour ne fonctionne jamais.

Et l’exception tombe au pire endroit : _après_ l’arrêt de l’ancien conteneur et la réécriture de la ligne en base, mais _avant_ `createIntegrationContainer()`. D’où l’incohérence base / conteneur décrite plus haut, plus les anciennes images jamais nettoyées.

Reproduction minimale du bug sous-jacent :

```auto
const StateManager = require('./server/lib/state');

```

`const sm = new StateManager();`

`const proxy = () => Object.freeze({ start: async () => {}, stop: async () => {} });`

`sm.setState('service', 'mon-service', proxy()); // ok`

`sm.setState('service', 'mon-service', proxy()); // TypeError: Cannot assign to read only property 'start'`

## Le contournement en attendant

```auto
docker rm -f gladys-ext-<mon-intégration>

```

Puis démarrer l’intégration depuis l’UI : `start.js` ne retrouve plus le conteneur et le recrée à partir du `docker_image` en base, donc avec la bonne version. À refaire à chaque mise à jour tant que ce n’est pas corrigé.

## Le correctif

Deux lignes, dans `registerProxyService()`, juste avant les deux `setState` — exactement ce que fait déjà `uninstall()` :

```auto
this.stateManager.deleteState('service', service.name);

```

`this.stateManager.deleteState('serviceById', service.id);`

`this.stateManager.setState('service', service.name, proxyService);`

`this.stateManager.setState('serviceById', service.id, proxyService);`

J’ai préféré ça à une modification de `Store.setState()` : changer « fusionner » en « remplacer » dans un helper partagé par tout le cœur est bien plus risqué que ces deux lignes locales au bug.

J’ai préparé le patch avec trois tests de non-régression : ils échouent sur `master` avec le `TypeError` exact et passent avec le correctif. Les suites `external-integration`, `state` et `service` passent intégralement (809 tests), prettier et eslint aussi. Je pousse la PR dans la foulée.

> <https://github.com/GladysAssistant/Gladys/pull/3127>
>
> \`stateManager.setState()\` merges into the existing \`Store\` instead of replacing …it, and the proxy service is built with \`Object.freeze()\`. So the first registration is fine (the \`Store\` state is still \`null\`, the object is assigned) while every later one does \`Object.assign(frozenProxy, newProxy)\` and throws on its first key:
> 
> TypeError: Cannot assign to read only property 'start' of object '#\<Object\>'
> 
> \`init()\` registers every installed external integration at boot and \`install()\` registers a freshly installed one, so by the time the user clicks "Update" the \`Store\` always exists and \`update()\` always throws.
> 
> It throws at a bad moment: \`registerProxyService()\` is called after the old container has been stopped and the \`t\_service\` row rewritten, but before \`createIntegrationContainer()\`. The update therefore left the new version, manifest and image in DB while \`container\_id\` still pointed at the old container, which the supervised restart then brought back up — the old code running under the new manifest, silently, and the replaced images never collected.
> 
> Drop the two state entries before setting them, exactly as \`uninstall()\` already does, so registering is safe to repeat.
> 
> 
> Claude-Session: https://claude.ai/code/session\_01CHCBWzjwWcFjyPuApwXFqX
> 
> \### Description
> 
> 
> 
> \### Related request
> 
> 
> 
> \### Checklist
> 
> \- \[\] If a forum topic or GitHub issue exists, the description links it (\`Forum: https://community.gladysassistant.com/t/...\` or \`Closes #...\`)
> \- \[\] Tests pass: \`cd server && npm run coverage\` (Codecov requires 100% coverage on changed lines) and Cypress (\`npm run cypress:run\`) if the UI changed
> \- \[\] Linter and prettier pass on both front and server (\`npm run eslint\`, \`npm run prettier\`)
> \- \[\] No undocumented breaking change
> 
> 
> 
> 
> 
> 
> \## Summary by CodeRabbit
> 
> \* \*\*Bug Fixes\*\*
> \* Fixed external integration updates when a proxy was already registered.
> \* Re-registering an integration proxy now completes without errors and uses the latest service version.
> \* Updated proxies correctly relay lifecycle messages and remain available after integration updates.
> \* Integration updates now continue successfully when manifest retrieval is unavailable, using the refreshed version information.

**Version :** Gladys 5.1.0, installation Docker Compose.

Merci !

---

<div class="post-metadata">

**Author:** ![pierre-gilles](https://gladys-assistant-community.b-cdn.net/user_avatar/community.gladysassistant.com/pierre-gilles/32/5886_2.png) [@pierre-gilles](https://community.gladysassistant.com/u/pierre-gilles)\
**Post date:** [Septembre 22, 2026, 6:14 UTC](https://community.gladysassistant.com/t/mettre-a-jour-une-integration-externe-echoue-toujours-la-nouvelle-version-saffiche-mais-lancienne-image-continue-de-tourner-5-1-0/10890/2 "2026-09-22T06:14:50Z")

</div>

Merci pour le retour, effectivement ! J’ai mergé ta PR et la 5.1.1 est en cours de build 🙂
