# Erratum — draft-fossil-sync-protocol-00 §8.1
| | |
|---|---|
| **Document** | `draft-fossil-sync-protocol-00` |
| **Section** | 8.1 — Clone Protocol Version (server handling of a SEQNO that is not digit-only) |
| **Type** | Compatibility note (no requirement on a conforming implementation changes) |
| **Status** | Proposed — operator decision pending |
| **Reported** | 2026-10-11 |
| **Submitter** | Dan Mestas |
| **Found via** | go-libfossil's sync conformance suite: fossil 2.28 [52445a27f1] answers `clone 3 -1` differently on macOS arm64 and Linux x64 builds |
## 1. Summary
§8.1 assigns `error invalid clone sequence number` only to a digit-only
SEQNO whose value is zero, and says the rule "does not assign that fatal
outcome to wrong arity, noninteger VERSION, or a SEQNO that fails
digit-only recognition". It says nothing about what such a SEQNO does
get. In Fossil 2.28 the answer is undefined: the server reads an
uninitialized variable. The same request, `clone 3 -1`, gets
`clone_seqno 0` from a macOS arm64 build and
`error invalid clone sequence number` from a Linux x64 build.
The draft's grammar (`clone-card = "clone" SP decimal SP decimal LF`) and
its clone loop (§8.2: SEQNO starts at 1 and is replaced only by a decimal
`clone_seqno`) already keep a conforming client from sending such a card,
so no requirement changes. The note records that Fossil gives no stable
answer, so that neither tests nor implementations treat either observed
reply as the reference behavior.
## 2. Location
- §8.1, second paragraph (~lines 1493-1500): "This rule does not assign
that fatal outcome to wrong arity, noninteger VERSION, or a SEQNO that
fails digit-only recognition or does not yield that condition."
## 3. The divergence
Fossil 2.28, `src/xfer.c:1550-1570`, in the `clone` handler:
```c
int seqno, max; /* seqno is not initialized */
...
blob_is_int(&xfer.aToken[2], &seqno); /* return value ignored */
if( seqno<=0 ){
xfer_fatal_error("invalid clone sequence number");
...
}
...
if( seqno>max ) seqno = 0;
@ clone_seqno %d(seqno)
```
`blob_is_int()` (`src/blob.c:1057-1071`) stores a value only when every byte
is an ASCII digit; for `-1`, `abc` or `1x` it returns 0 and leaves `seqno`
as whatever the stack held. A non-positive leftover gives the fatal error
and rollback; a positive one greater than the largest RID gives no `cfile`
and `clone_seqno 0`; a positive one within range would serve artifacts
from an arbitrary RID.
Observed on this machine (fossil 2.28 [52445a27f1], Homebrew, macOS arm64),
`fossil http` with an `application/x-fossil-debug` request, three runs each:
| SEQNO | Reply |
|---|---|
| `0` | `error invalid\sclone\ssequence\snumber` |
| `-1` | `clone_seqno 0`, `push SERVERCODE PROJECTCODE`, timestamp |
| `abc` | same as `-1` |
| `1x` | same as `-1` |
The Linux x64 reply (`error invalid\sclone\ssequence\snumber` for
`clone 3 -1`) is from the go-libfossil suite's run on that platform and was
not reproduced here.
## 4. Impact
None for a conforming client, which cannot send the card. A test that pins
either reply for a non-digit SEQNO passes against one Fossil build and
fails against another, and a server implementer looking to Fossil for the
"compatible" answer finds none.
## 5. Correction
In §8.1, append to the second paragraph:
```diff
parsing later request cards. This rule does not assign that fatal
outcome to wrong arity, noninteger VERSION, or a SEQNO that fails
digit-only recognition or does not yield that condition.
+ Fossil gives no stable reply to a SEQNO that is not digit-only: it
+ tests an uninitialized value, so builds differ between the fatal
+ error, an empty clone_seqno 0 reply, and serving from an arbitrary
+ RID. Such a card does not match the clone-card grammar (Appendix B)
+ and is never produced by the client loop of Section 8.2; clients
+ MUST send a positive decimal SEQNO, and neither a client nor a test
+ may rely on any particular reply to another value.
```
## 6. Rationale
- **No new obligation.** The grammar and the clone loop already confine a
client to a positive decimal SEQNO; the MUST makes that explicit at the
card's definition, where a reader looks for it.
- **Removes a false oracle.** Recording that Fossil's reply depends on an
uninitialized variable stops either platform's output from being taken
as the protocol's answer.
## 7. Verification
- Fossil 2.28 [52445a27f1], macOS arm64: the table above, three runs per
SEQNO, `nobody` holding `og`.
- Source references are to `src/xfer.c` and `src/blob.c` at tag
`version-2.28`.
- Not verified here: the Linux x64 reply, taken from the go-libfossil
suite's report.
---
*Prepared against `draft-fossil-sync-protocol-00`. The RFC remains the oracle; the reference implementation is used only to confirm the observable behavior.*