fix(android): a conflict answers 422, not 409

The retry path finally ran, and did not recognise what it was handed. A
desktop publishing between our read and our write earns a 422, not a 409, so
the save failed instead of retrying - which is exactly what a never-exercised
path hides: it was written, it compiled, it was even commented, and it did
not fire.

Gitea answers 409 when the sha no longer matches and 422 when it is missing
or the request is judged unprocessable, "file already exists" among them.
Both say the same thing to us: the sha is stale. Re-reading and retrying is
right for both and cannot clobber anything - the retry publishes against the
fresh sha. A genuinely malformed body would fail again and give up after
three tries, carrying Gitea's own words.

The log now prints which code it was, rather than assuming.
This commit is contained in:
r-zakarya
2026-08-21 16:56:48 +01:00
parent 31bf4f920c
commit 30b2413ea3
3 changed files with 34 additions and 8 deletions
+18 -5
View File
@@ -843,11 +843,24 @@ Les trois verbes d'un coup, et depuis l'autre bout : **créer** (4), **modifier*
(1), **supprimer** (1). Le `1 deleted` est celui qui comptait — la disparition
locale ne prouvait rien, c'est la pierre tombale qui devait voyager.
Les étapes 1 à 3 de cette section sont donc closes. **Reste le 409** : depuis
que l'enregistrement ne tire plus avant de publier, c'est lui seul qui garantit
qu'une écriture du téléphone n'écrase pas le poste, et il n'a toujours jamais
tourné. Le provoquer : synchroniser depuis le poste, puis enregistrer depuis le
téléphone sans synchroniser d'abord.
Les étapes 1 à 3 de cette section sont donc closes.
### Le conflit rend 422, pas 409 — corrigé 2026-08-21
Le chemin de reprise a enfin tourné, et il ne reconnaissait pas ce qu'il
recevait. Un poste qui publie entre notre lecture et notre écriture nous vaut un
**422**, pas un 409 : l'enregistrement échouait au lieu de reprendre.
Gitea rend 409 quand le sha envoyé ne correspond plus, et 422 quand il manque ou
que la demande est jugée irrecevable — dont le cas « ce fichier existe déjà »
d'une création. Les deux disent la même chose pour nous : **le sha n'est plus le
bon**. Relire et recommencer est la bonne réponse aux deux, et elle est sans
danger — la reprise republie avec le sha frais, donc elle n'écrase jamais rien ;
un corps réellement mal formé échouerait de nouveau et sortirait au bout de
trois essais, avec le texte de Gitea.
C'est exactement ce qu'un chemin jamais exercé cache : il était écrit, il
compilait, il était même commenté — et il ne se déclenchait pas.
### Ce qui devient facultatif
+14 -2
View File
@@ -40,7 +40,7 @@ type
ssRefused, // 403 : HTTPS exige par le serveur
ssHttpError, // autre code
ssNetworkError, // rien n'a repondu
/// 409 : le sha envoye n'est plus celui du fichier - un autre appareil a
/// 409 ou 422 : le sha envoye n'est plus celui du fichier - un autre appareil a
/// publie entre notre lecture et notre ecriture. L'appelant doit relire,
/// refusionner et renvoyer, pas reessayer tel quel.
ssConflict
@@ -232,7 +232,19 @@ begin
// prochaine ecriture - sans lui il faudrait relire pour publier.
Result.ETag := ShaFromPutResponse(LResync.DataString);
end;
409: Result.Status := ssConflict;
// 409 ET 422 : DANS LES DEUX CAS LE SHA N'EST PLUS LE BON.
//
// Gitea rend 409 quand le sha envoye ne correspond plus, et 422 quand
// il manque ou qu'il juge la demande irrecevable - dont le cas « ce
// fichier existe deja » d'une creation. Mesure a l'appui : un poste
// qui publie entre notre lecture et notre ecriture nous a valu un
// 422, pas un 409, et l'enregistrement echouait au lieu de reprendre.
//
// Relire et recommencer est la bonne reponse aux deux, et elle est
// sans danger : la reprise republie avec le sha frais, donc elle
// n'ecrase jamais rien. Un vrai corps mal forme echouerait de nouveau
// et sortirait au bout de trois essais, avec le texte de Gitea.
409, 422: Result.Status := ssConflict;
else
Result.Status := ssHttpError;
// LE CORPS, TOUJOURS. Il etait remplace par un message fige sur 401
+2 -1
View File
@@ -832,7 +832,8 @@ begin
if LPut.Status = ssConflict then
begin
Log('save: 409 - another device published, re-reading');
Log(Format('save: %d - another device published, re-reading',
[LPut.HttpCode]));
LPlain := '';
Continue;
end;