systemctl status=203/EXEC : execve a échoué et le programme n’a jamais démarré

Le journal a écrit Main process exited, code=exited, status=203/EXEC et le service est passé en failed sans qu’une seule ligne de votre programme s’exécute. Le code 203 est interne à systemd : la page de manuel systemd.exec(5) livrée par Debian 13 le nomme EXIT_EXEC et le rattache à l’appel système execve(2). Quatre causes couvrent la quasi-totalité des cas, et la première ligne du journal les sépare déjà : cible introuvable, bit d’exécution manquant, format de fichier refusé, interpréteur absent.
Sommaire
- code=exited, status=203/EXEC vient de systemd et place la panne dans execve
- Trois causes produisent 203/EXEC : cible absente, bit x perdu, format refusé
- Reprendre la main : nommer le chemin, tester l’accès, corriger, relancer
- Le piège : No such file or directory désigne parfois la ligne #! et non le fichier
- Failed to locate en 252, Unable to locate en 257 : la formulation dépend de la version
- Le bit x, le shebang et le noexec couvrent la plupart des 203/EXEC : six questions pour trancher
- Sources et versions de référence
code=exited, status=203/EXEC vient de systemd et place la panne dans execve
Le statut 203/EXEC est fabriqué par systemd, pas par votre programme : l’appel système execve(2) a échoué et le noyau n’a jamais transféré la main au binaire. La page de manuel systemd.exec(5) livrée par Debian 13 nomme ce code EXIT_EXEC et le résume par cette phrase : « The actual process execution failed (specifically, the execve(2) system call). Most likely this is caused by a missing or non-accessible executable file. » La fonction unit_log_process_exit() écrit le nombre puis son nom, 203 puis EXEC, d’après le code de la balise v252 du dépôt systemd. Un programme qui plante en interne renvoie son propre code de sortie, sans nom symbolique accolé, et systemd écrit alors status=1 ou status=2. Voir 203/EXEC circonscrit donc la panne à la phase de lancement, avant la première instruction exécutée, ce que la suite du journal confirme en nommant le fichier refusé.
sep 19 10:12:33 poste systemd[1]: sauvegarde.service: Failed to locate executable /usr/local/bin/sauvegarde.sh: No such file or directory sep 19 10:12:33 poste systemd[1]: sauvegarde.service: Failed at step EXEC spawning /usr/local/bin/sauvegarde.sh: No such file or directory sep 19 10:12:33 poste systemd[1]: sauvegarde.service: Main process exited, code=exited, status=203/EXEC sep 19 10:12:33 poste systemd[1]: sauvegarde.service: Failed with result 'exit-code'.
Cause de ces quatre lignes : le chemin écrit dans ExecStart ne mène à aucun fichier. Le remède tient en deux gestes, vérifier le chemin avec ls -l, puis réécrire la ligne ExecStart. Sur systemd 257, la même panne s’écrit Unable to locate executable '/usr/local/bin/sauvegarde.sh': No such file or directory, la deuxième ligne ne bougeant pas d’une version à l’autre.
Trois causes produisent 203/EXEC : cible absente, bit x perdu, format refusé
execve(2) ne renvoie qu’une poignée d’erreurs, et chacune se traduit par une phrase différente dans le journal. Un chemin qui ne mène à rien produit ENOENT, que le noyau nomme No such file or directory ; un fichier privé du bit d’exécution produit EACCES, traduit Permission denied ; un fichier qui n’est ni un binaire ELF reconnu ni un script à ligne #! produit ENOEXEC, soit Exec format error. Selon la page execve(2) du projet man-pages Linux, ces erreurs couvrent aussi l’option de montage noexec et le cas d’un interpréteur absent, script ou chargeur ELF, qui trompe tout le monde parce que le fichier visé, lui, existe. Sur systemd 252, la fonction check_x_access() de path-util.c tranche avant même l’appel au noyau : elle ouvre le chemin, vérifie qu’il s’agit d’un fichier ordinaire, puis teste le droit d’exécution, et son verdict alimente la première ligne du journal.
| Cause constatée | Errno exécve | Ligne du journal sur systemd 252 | Correction |
|---|---|---|---|
| Chemin absent | ENOENT | Failed to locate executable /usr/local/bin/sauvegarde.sh: No such file or directory |
corriger ExecStart, puis systemctl daemon-reload |
| Bit x manquant | EACCES | Failed to locate executable /usr/local/bin/sauvegarde.sh: Permission denied |
chmod 755 /usr/local/bin/sauvegarde.sh |
| Chemin qui désigne un répertoire | EISDIR | Failed to locate executable /usr/local/bin/sauvegarde.sh: Is a directory |
corriger le chemin dans l’unité |
| Partition montée noexec | EACCES | Failed to execute /usr/local/bin/sauvegarde.sh: Permission denied |
déplacer le fichier ou remonter la partition avec exec |
| Ligne #! terminée par un retour chariot | ENOENT | Failed to execute /usr/local/bin/sauvegarde.sh: No such file or directory |
sed -i 's/\r$//' /usr/local/bin/sauvegarde.sh |
| Script sans ligne #! | ENOEXEC | Failed to execute /usr/local/bin/sauvegarde.sh: Exec format error |
ajouter #!/bin/bash en première ligne |
| Chargeur ELF absent | ENOENT | Failed to execute /usr/local/bin/binaire: No such file or directory |
fournir libc6:i386 ou recompiler pour la bonne architecture |
Le contrôle de systemd précède l’appel au noyau, et il ne voit pas tout : l’option noexec d’une partition n’est testée qu’au moment d’exécuter, dans may_open() du fichier fs/namei.c du noyau Linux. D’où la ligne Failed to execute pour ce cas, là où un bit x manquant produit Failed to locate.
Reprendre la main : nommer le chemin, tester l’accès, corriger, relancer
Le diagnostic se mène dans l’ordre, sans rien modifier avant la dernière étape : la ligne du journal nomme le message, l’unité donne le chemin exact, le disque dit si le fichier est exécutable, le noyau dit s’il accepte le format, et la table de montage dit si la partition autorise l’exécution. Chaque étape se termine par la sortie attendue, qui sert de critère d’arrêt. Les exemples utilisent sauvegarde.service et /usr/local/bin/sauvegarde.sh ; remplacez ces deux noms par ceux de votre unité, que systemctl cat affiche avec ses éventuels drop-ins. Tant qu’une sortie ne correspond pas à ce qui est annoncé, la cause n’est pas encore trouvée, et mieux vaut continuer la liste que corriger au hasard.
-
Lire l’état du service et la dernière erreur.
systemctl status sauvegarde.service --no-pagerCe que vous devez voir : une ligne
Active: failed (Result: exit-code), une ligneProcess: 4821 ExecStart=/usr/local/bin/sauvegarde.sh (code=exited, status=203/EXEC), puis les dernières lignes du journal. -
Extraire le chemin exact lu par systemd.
systemctl cat sauvegarde.serviceCe que vous devez voir : la ligne
ExecStart=/usr/local/bin/sauvegarde.sh, avec un chemin complet. -
Vérifier l’existence du fichier et son bit d’exécution.
ls -l /usr/local/bin/sauvegarde.sh test -x /usr/local/bin/sauvegarde.sh && echo "bit x present"Ce que vous devez voir : un mode qui commence par
-rwxet le textebit x present. Un mode-rw-r--r--annonce Permission denied. -
Lire le type réel du fichier et les octets de la première ligne.
file /usr/local/bin/sauvegarde.sh head -1 /usr/local/bin/sauvegarde.sh | od -c | head -3Ce que vous devez voir :
POSIX shell script, ASCII text executable, sans la mentionwith CRLF line terminators, et un\nen fin de ligne, jamais\r \n. -
Vérifier la monture et les réglages qui masquent un chemin.
findmnt -no SOURCE,OPTIONS -T /usr/local/bin/sauvegarde.sh grep -E "ProtectHome|RootDirectory" /etc/systemd/system/sauvegarde.serviceCe que vous devez voir : des options de montage sans
noexec, et aucune ligneProtectHome=yesquand le binaire vit sous /home. -
Corriger, recharger si l’unité a changé, relancer.
sudo chmod 755 /usr/local/bin/sauvegarde.sh sudo systemctl daemon-reload sudo systemctl restart sauvegarde.service systemctl status sauvegarde.service --no-pagerCe que vous devez voir :
Active: active (running)et, dans le journal,Started sauvegarde.serviceà la place deFailed with result 'exit-code'.
Le piège : No such file or directory désigne parfois la ligne #! et non le fichier
Un fichier présent, lisible et marqué exécutable peut tout de même provoquer No such file or directory, parce que le noyau ne cherche pas que le fichier : il cherche aussi l’interpréteur que la ligne #! désigne. D’après fs/binfmt_script.c dans le noyau Linux, cette ligne est lue jusqu’au premier passage à la ligne, seuls les espaces et les tabulations de fin sont retirés, et le nom restant est pris au mot dans un tampon de 256 octets. Un \r laissé par un éditeur venu de Windows reste donc collé au nom : le noyau demande /bin/bash suivi d’un retour chariot, ne trouve rien, et rend ENOENT. Un second cas donne le même message sans ligne #! : un binaire ELF dont le chargeur dynamique manque, par exemple un exécutable 32 bits sur un système 64 bits dépourvu de libc6:i386, car execve(2) rend ENOENT dès qu’un interpréteur ELF manque.
file /usr/local/bin/sauvegarde.sh
head -1 /usr/local/bin/sauvegarde.sh | od -c | head -3
sed -i 's/\r$//' /usr/local/bin/sauvegarde.sh
La première commande nomme le type réel, la deuxième montre les octets de la ligne #!, la troisième retire les retours chariot. Un interpréteur /bin/bash existe sous Debian et Ubuntu, où /bin mène à /usr/bin, comme le détaillent les chemins de /bin et de bash.
Failed to locate en 252, Unable to locate en 257 : la formulation dépend de la version
La phrase à chercher dans le journal a changé deux fois entre systemd 245 et systemd 257, et une recherche sur un texte exact ne trouve donc pas les réponses écrites pour une autre version. De systemd 237 à 245, la première ligne était Failed to execute command, sans le chemin, et le nom du fichier n’apparaissait que sur la ligne Failed at step EXEC spawning, d’après les balises v237 et v245 du dépôt systemd. À partir de systemd 247, cette première ligne devient Failed to locate executable /chemin, réservée aux cas où la cible n’est pas trouvable ou pas exécutable. La balise v256 remplace la phrase par Unable to locate executable ‘/chemin’, entre apostrophes, et Debian 13 livre en 257.13-1~deb13u1 cette dernière formulation, publiée le 9 août 2025.
| Version de systemd | Distribution qui l’embarque | Première ligne du journal |
|---|---|---|
| 252.39-1~deb12u2 | Debian 12 bookworm | Failed to locate executable /usr/local/bin/sauvegarde.sh: No such file or directory |
| 255.4 | Ubuntu 24.04 LTS | même première ligne que systemd 252 |
| 257.13-1~deb13u1 | Debian 13 trixie | Unable to locate executable '/usr/local/bin/sauvegarde.sh': No such file or directory |
Deux limites valent d’être connues. La page systemd.exec(5) résume encore 203 par missing or non-accessible executable file, sans reprendre la séparation entre Failed to locate et Failed to execute que le code applique depuis systemd 252.
La lecture des unités et des journaux pour un autre service suit la même marche, décrite dans systemctl service code exited status 203, et le reste du diagnostic tient dans la rubrique Linux.
Le bit x, le shebang et le noexec couvrent la plupart des 203/EXEC : six questions pour trancher
Le fichier existe, porte le bit x, et le journal dit quand même No such file or directory ?
La ligne #! désigne alors un interpréteur que le noyau ne trouve pas : un \r collé au nom transforme /bin/bash en /bin/bash\r, qui n’existe pas. La commande file affiche « with CRLF line terminators », head -1 | od -c montre le \r, et sed -i 's/\r$//' le retire. Même message pour un binaire 32 bits privé de son chargeur.
Faut-il écrire /usr/bin/bash ou seulement bash dans ExecStart ?
Selon systemd.service(5), les deux passent : un chemin absolu, ou un simple nom sans barre oblique, résolu dans un chemin de recherche figé à la compilation, qui contient /usr/local/bin, /usr/bin et leurs équivalents sbin. La commande systemd-path search-binaries-default affiche ce chemin réel. Hors de ces répertoires, seul un chemin absolu évite le 203/EXEC.
Comment réparer un 203/EXEC quand le bit x manque ?
chmod 755 /usr/local/bin/sauvegarde.sh puis systemctl restart sauvegarde.service suffisent, aucun rechargement de l’unité n’étant nécessaire si le fichier .service n’a pas bougé. Le journal doit passer de Failed with result 'exit-code' à Started sauvegarde.service. Si l’unité tourne sous un autre compte avec User=, contrôler le chemin complet avec namei -l.
Faut-il un daemon-reload après la correction ?
Uniquement si le fichier .service a été modifié : selon systemd.unit(5), systemctl daemon-reload remplace la configuration d’une unité déjà chargée. Un chmod, une correction de shebang ou un fichier remplacé ne touchent pas l’unité, donc systemctl restart suffit. Après une correction d’ExecStart, l’ordre est daemon-reload puis restart, et systemctl reset-failed sauvegarde.service remet à zéro le compteur de démarrages.
Combien de tentatives systemd fait-il avant d’abandonner ?
Cinq en dix secondes par défaut, d’après systemd-system.conf(5) qui fixe DefaultStartLimitBurst à 5 et DefaultStartLimitIntervalSec à dix secondes. Au-delà, le journal écrit Start request repeated too quickly. et l’unité reste en failed sans nouvel essai. Relever StartLimitBurst ne répare pas un 203/EXEC : le binaire reste introuvable.
Un script sans ligne #! peut-il démarrer sous systemd ?
Non. Le noyau rend ENOEXEC pour un fichier qui n’est ni un format binaire reconnu ni un script à ligne #!, et systemd n’a pas de repli vers un shell : le journal écrit Failed to execute /usr/local/bin/sauvegarde.sh: Exec format error, suivi de 203/EXEC. Ajouter #!/bin/bash en première ligne règle le cas, sinon ExecStart=/bin/bash /usr/local/bin/sauvegarde.sh.
Sources et versions de référence
- systemd.exec(5), page livrée par Debian 13 : code 203 EXIT_EXEC, formulation missing or non-accessible executable file, réglage ProtectHome=
- execve(2), projet man-pages : EACCES, ENOENT, ENOEXEC et option de montage noexec
- fs/binfmt_script.c, noyau Linux : lecture de la ligne #! jusqu’au premier passage à la ligne
- exec-invoke.c, dépôt systemd, balise v257 : lignes Unable to locate executable et Failed to execute
- Paquet systemd 257.13-1~deb13u1, Debian 13 trixie