Aller au contenu

epitech-tracker — Erreurs courantes

Peu d'incidents de prod documentés dans ce dépôt

Contrairement à EPIDOCS API, ce dépôt ne contient pas de journal d'incidents de production détaillé au fil de l'eau. Les entrées ci-dessous sont soit des incidents réels déjà analysés (docs/ANALYSE_PROD_2026-09-08.md, commits de fix), soit des modes d'échec directement lisibles dans le code (gestion d'erreur explicite dans sender.py, identity.py, updater.py, watcher.py) — jamais des scénarios inventés. Chaque entrée cite le fichier source concerné.

Enrôlement et identité

Le service tourne mais aucune donnée n'arrive jamais côté Zeno

Symptôme : systemctl status epitech-tracker est active (running), mais rien n'apparaît jamais côté plateforme parentale pour ce poste.

Cause (src/identity.py, src/sender.py) : /etc/epitech-tracker/identity.json est absent, vide, ou ne contient pas deviceId/key. sender.send() détecte l'absence d'identité et conserve le snapshot en queue au lieu de l'envoyer — indéfiniment, jusqu'à purge à 7 jours :

ident = load_identity()
if not ident:
    log.error("Machine non enrôlée — snapshot conservé en queue (ré-enrôler).")
    return False

Diagnostic :

sudo cat /etc/epitech-tracker/identity.json
journalctl -u epitech-tracker --since "1 hour ago" | grep -i "non enrôlée\|non enrolee"

Solution : ré-enrôler le poste (voir runbook.md).

Bug réel déjà corrigé — ne pas le réintroduire

Avant e0bd01f fix(tracker): l'identité d'un poste n'exige plus de mail Epitech, load_identity() exigeait un champ email, alors qu'install.sh enrôle par nom + prénom et ne l'écrit pas pour la majorité des promos. Conséquence : sender.send traitait chaque poste fraîchement installé comme non enrôlé — l'installation semblait réussie, le service tournait, mais la donnée s'évaporait silencieusement à la purge de 7 jours (test_identity.py, docstring du module, en garde la trace). Si ce symptôme revient, vérifier en premier si un champ jugé "obligatoire" dans identity.py a été réintroduit sans tenir compte de l'enrôlement par nom.

Rejets 401/403 à l'envoi

Cause (src/sender.py) : device inconnu côté serveur, token/horloge désynchronisés, ou signature invalide (souvent un ré-enrôlement en attente côté serveur). Le code traite ces deux codes comme des échecs transitoires :

if code == 403:
    log.error("Rejeté (403) — signature invalide ; snapshot conservé en queue (ré-enrôler ?).")
    return False
if code == 401:
    log.error("Rejeté (401) — device/token/horloge ; snapshot conservé en queue : %s", response.text[:200])
    return False

Le snapshot n'est jamais jeté sur ces codes — il repart au prochain flush une fois la cause corrigée, re-signé avec l'identité courante du poste (utile si le poste a été ré-enrôlé entre-temps).

Diagnostic : journalctl -u epitech-tracker | grep "Rejeté (40". Si persistant sur un seul poste → ré-enrôler. Si sur toute la flotte → suspecter une régression côté Zeno (vérification de signature, horloge serveur) plutôt qu'un problème local.

Réseau et API Zeno injoignable

Cause (src/sender.py) : ConnectionError/Timeout (réseau coupé, DNS, Zeno down) → le snapshot part en queue sans qu'aucune erreur ne remonte plus haut que main.py, qui logue et continue sa boucle.

except (ConnectionError, Timeout):
    log.debug("Hors ligne — snapshot sera mis en queue")
    return False

Pas de retry avec backoff : le README liste ceci comme TODO haute priorité (sender.py — retry avec backoff exponentiel). Aujourd'hui, une coupure réseau de quelques secondes fait directement passer le snapshot en queue plutôt que de retenter dans le même cycle — sans conséquence pratique tant que la coupure reste courte (le cycle suivant, 5 minutes plus tard, tentera un envoi direct puis un flush_queue()), mais ce n'est pas optimisé pour des micro-coupures fréquentes.

Limite structurelle de la queue (src/queue_manager.py) : rétention 7 jours et 50 Mo max, les plus anciens fichiers étant évincés en premier dans les deux cas. Une panne Zeno de plus d'une semaine, ou un poste hors ligne prolongé (vacances, laptop rangé), fait perdre silencieusement les snapshots les plus anciens — voir runbook.md pour la procédure fleet-wide.

Mise à jour automatique (updater.py)

Le poste ne se met jamais à jour

Causes possibles, par ordre de vraisemblance :

  1. sha256 publié invalide ou paquet corrompu en transit — updater._download_and_verify() rejette explicitement tout paquet dont le sha256 ne correspond pas à celui annoncé par GET /sonde/version, ou dont la taille diffère, ou qui fait moins de 1024 octets (« manifestement tronqué ») :
    if got != expected_sha:
        log.error("Paquet REJETÉ — sha256 %s attendu, %s obtenu", expected_sha, got)
        return None
    
    C'est volontaire et non contournable : c'est la seule protection contre un paquet substitué en transit. Un poste qui ne se met jamais à jour alors que Zeno a bien publié une nouvelle version doit faire suspecter une empreinte mal recalculée côté Zeno après le dépôt de tools.deb, pas un bug du poste.
  2. Réponse de version incomplète — si GET /sonde/version ne renvoie pas version et sha256, _published_version() retourne None sans rien tenter.
  3. dpkg -i échoue (dépendances) — updater.py tente un apt-get -f install -y de rattrapage ; si ça échoue aussi, la mise à jour n'a pas lieu, journalisé mais sans alerte active.

Diagnostic :

journalctl -u epitech-tracker | grep -i "vérification mise à jour\|REJETÉ\|dpkg -i échoué"

Un poste sur un dépôt public personnel, pas Zeno

Contexte historique (.github/workflows/release.yml, README §7) : les postes installés avant la version 1.1.0 embarquent un updater qui interroge api.github.com/repos/YemalinModeste/epitech-tools — un dépôt public appartenant personnellement à Yémalin, distinct de l'organisation EpitechAfrik — et ignorent totalement Zeno. Un tel poste ne recevra jamais de mise à jour publiée normalement (via Zeno). La procédure de bascule ponctuelle (workflow_dispatch avec mirror_public: true) est documentée dans le README et dans runbook.md. Voir aussi continuite-acces-externes.md pour l'implication en termes de continuité (ce dépôt public n'a qu'un seul collaborateur).

Détection de l'activité (watcher / observer)

app_active reste "unknown" en continu sur un poste

Cause (src/watcher.py) : le WindowWatcher n'a pas encore trouvé de session graphique humaine exploitable (pas de DISPLAY/WAYLAND_DISPLAY + DBUS_SESSION_BUS_ADDRESS détectables dans /proc/<pid>/environ) — typique d'un poste resté sur l'écran de login, ou d'une session sans bus a11y actif. Le watchdog interne retente toutes les 30 secondes, mais abandonne après 10 tentatives consécutives d'échec de spawn :

_WATCHDOG_MAX_RETRIES = 10
...
if retries >= _WATCHDOG_MAX_RETRIES:
    log.error("WindowWatcher abandonné après %d tentatives", _WATCHDOG_MAX_RETRIES)

Après abandon, le watcher ne se relance pas tout seul — un redémarrage du service (systemctl restart epitech-tracker) est nécessaire une fois la session graphique de l'étudiant établie. observer.collect_snapshot() a un repli ponctuel (get_active_app) si le watcher rend "unknown", donc le snapshot n'est pas totalement vide, mais il perd le suivi fin par changement de fenêtre (fenetre_sessions).

Diagnostic :

journalctl -u epitech-tracker | grep -i "windowwatcher"

Projet git actif toujours null alors que l'étudiant travaille visiblement dessus

Causes possibles (src/detector.py, TODO README) :

  1. Wayland + VS Code : detector.py s'appuie sur /proc/<pid>/cmdline et /proc/<pid>/cwd pour retrouver le dossier ouvert par l'éditeur — documenté comme limité sur Wayland pour VS Code (le README le liste en TODO haute priorité : lire le socket Unix de VS Code ou parser ~/.config/Code/User/workspaceStorage/).
  2. Remote non conforme au format EPITECH : _EPITECH_REPO_RE n'accepte que des noms de dépôt de la forme <PREFIXE>-<MODULE>-<NUM>-<VILLE>-<N>-<N>-<projet>[-<N>] (ex: G-CPE-110-ABJ-1-1-organized-2). Un dépôt cloné sous un autre nom, ou un remote non-EPITECH, ne matche pas et projet_actif reste null — comportement attendu, pas un bug.
  3. Ownership git : le service tourne en root sur des dépôts appartenant à l'étudiant ; sans -c safe.directory=* (déjà forcé sur chaque appel git dans detector.py), git refuserait avec "detected dubious ownership" et projet_actif resterait null systématiquement. Si ce comportement est réapparu, vérifier que _GIT_SAFE est toujours appliqué à chaque subprocess.run(["git", ...]).

Historique : sous-comptage massif du temps de développement (résolu)

Incident réel, chiffré, déjà corrigé — utile pour comprendre pourquoi l'architecture a changé

docs/ANALYSE_PROD_2026-09-08.md documente une analyse de 43 874 snapshots de prod ayant révélé que le temps de développement réel était sous-compté d'un facteur ~5 (6,5 % mesuré vs 35,3 % après correction), à cause de plusieurs bugs de normalisation locale : "Google Chrome".split()[0] → "google", qui ne matchait alors aucune catégorie connue et tombait en "inactif" ; emacs lancé dans un terminal jamais compté comme "dev" (l'app au premier plan restant le terminal) ; catalogue d'applications périmé (antigravity, claude-desktop non reconnus). Corrigé par 6473cbb, puis rendu structurellement impossible à re-dériver par le refactor c6ea729 qui a déplacé toute la catégorisation côté serveur (voir overview.md). Si un futur symptôme ressemble à "le score d'effort semble anormalement bas", vérifier d'abord la logique d'agrégation/catégorisation côté Zeno, pas dans ce dépôt — la sonde n'y participe plus.

Immuabilité anti-tamper et débogage manuel

Symptôme : un dpkg -i manuel ou une suppression manuelle de fichiers sous /opt/epitech-tracker/ échoue avec une erreur de permission alors que la commande est lancée en root.

Cause (package/DEBIAN/postinst) : postinst pose chattr +i (immuable) sur /usr/lib/systemd/system/epitech-tracker.service et -R +i sur /opt/epitech-tracker/ pour empêcher un étudiant root sur sa propre machine de neutraliser trivialement le service. Un dpkg normal (upgrade/removal via le paquet) lève cet attribut lui-même (prerm), mais une intervention manuelle en dehors de dpkg ne le fait pas.

Solution :

sudo chattr -i /usr/lib/systemd/system/epitech-tracker.service
sudo chattr -R -i /opt/epitech-tracker/