Summary
npm provenance publishing can fail with:
TLOG_CREATE_ENTRY_ERROR error creating tlog entry - (409) an equivalent entry already exists in the transparency log
This appears to be a retry-after-success path. Rekor accepts and commits the create-entry request, but the client times out, receives a transient network failure, or retries after a retryable response. The retry submits the same equivalent entry, Rekor correctly returns 409, and the signing flow treats that as fatal.
Current behavior
TLogClient defaults fetchOnConflict to false:
this.fetchOnConflict = options.fetchOnConflict ?? false;
High-level sigstore.attest() / npm provenance callers usually do not set this option directly, so a 409 from an already-created Rekor entry becomes TLOG_CREATE_ENTRY_ERROR instead of using the existing recovery path.
Expected behavior
When Rekor returns a 409 with a location for an equivalent existing entry, the default behavior should fetch the existing entry and continue. The existing entry is the entry the client needs in the Sigstore bundle, so this makes the create-entry operation idempotent from the caller's perspective while preserving provenance.
Callers that need fatal conflict behavior can still pass fetchOnConflict: false explicitly.
Downstream reports
Similar downstream failures and analyses:
Proposed fix
Default TLogClient to fetchOnConflict: true, while preserving explicit fetchOnConflict: false for callers that want conflicts to remain fatal.
Summary
npm provenance publishing can fail with:
This appears to be a retry-after-success path. Rekor accepts and commits the create-entry request, but the client times out, receives a transient network failure, or retries after a retryable response. The retry submits the same equivalent entry, Rekor correctly returns 409, and the signing flow treats that as fatal.
Current behavior
TLogClientdefaultsfetchOnConflicttofalse:High-level
sigstore.attest()/ npm provenance callers usually do not set this option directly, so a 409 from an already-created Rekor entry becomesTLOG_CREATE_ENTRY_ERRORinstead of using the existing recovery path.Expected behavior
When Rekor returns a 409 with a location for an equivalent existing entry, the default behavior should fetch the existing entry and continue. The existing entry is the entry the client needs in the Sigstore bundle, so this makes the create-entry operation idempotent from the caller's perspective while preserving provenance.
Callers that need fatal conflict behavior can still pass
fetchOnConflict: falseexplicitly.Downstream reports
Similar downstream failures and analyses:
Proposed fix
Default
TLogClienttofetchOnConflict: true, while preserving explicitfetchOnConflict: falsefor callers that want conflicts to remain fatal.