siyuan-note/siyuan · error
mobile export lease [%s] already exists
Error message
mobile export lease [%s] already exists
What it means
Mobile export leases are tracked in an in-process map keyed by leaseID. registerMobileExportLeaseWithID inserts under a mutex and refuses to overwrite an existing lease ID, returning this error when the ID is already live. This prevents one holder's release/TTL cleanup from destroying another holder's lease state.
Source
Thrown at kernel/model/encrypted_export.go:378
}
func registerMobileExportLeaseWithID(leaseID, boxID, artifact, name, cleanupDir string) (*MobileExportLease, error) {
info, err := os.Stat(artifact)
if err != nil {
return nil, err
}
if info.IsDir() {
return nil, fmt.Errorf("export artifact [%s] is a directory", artifact)
}
state := &mobileExportLeaseState{
boxID: boxID,
cleanupDir: cleanupDir,
expiresAt: time.Now().Add(mobileExportLeaseTTL),
}
mobileExportLeases.Lock()
if _, exists := mobileExportLeases.leases[leaseID]; exists {
mobileExportLeases.Unlock()
return nil, fmt.Errorf("mobile export lease [%s] already exists", leaseID)
}
mobileExportLeases.leases[leaseID] = state
state.timer = time.AfterFunc(mobileExportLeaseTTL, func() {
releaseMobileExportLease(leaseID, true)
})
mobileExportLeases.Unlock()
return &MobileExportLease{ID: leaseID, Path: artifact, Name: name, Size: info.Size()}, nil
}
// ReleaseExportArtifactLease 释放导出产物租约;重复调用不会产生副作用。
func ReleaseExportArtifactLease(leaseID string) {
releaseMobileExportLease(leaseID, false)
}
// ReleaseMobileExportLease 释放移动端导出租约;重复调用不会产生副作用。
func ReleaseMobileExportLease(leaseID string) {
ReleaseExportArtifactLease(leaseID)
}View on GitHub (pinned to 8641553a1f)
Solutions
- Generate a fresh unique lease ID (random/UUID) per request instead of reusing an existing one.
- Release the prior lease first (call the release API) or wait for the TTL expiry, then re-acquire.
- Treat this as a benign conflict: catch it and fetch/reuse the already-existing lease rather than re-registering.
Example fix
// before
lease, err := model.AcquireMobileExportLeaseWithID("lease-1", boxID, artifact)
// after
leaseID := uuid.NewString()
lease, err := model.AcquireMobileExportLeaseWithID(leaseID, boxID, artifact) Defensive patterns
Strategy: try-catch
Try / catch
lease, err := registerMobileExportLeaseWithID(id, boxID, artifact, name, dir)
if err != nil && strings.Contains(err.Error(), "already exists") {
lease = getExistingLease(id) // reuse instead of re-registering
} Prevention
- Generate unique lease IDs (UUID) per request
- Release leases promptly; do not wait only for TTL expiry
- Make retries idempotent by fetching the existing lease on conflict
When it happens
Trigger: Calling registerMobileExportLease / AcquireExportArtifactLease twice with the same explicit lease ID without the first lease being released (explicitly or via the mobileExportLeaseTTL timer); a client reusing a cached leaseID after the first request succeeded.
Common situations: A mobile client retries an export request with a deterministic ID before the previous lease expired; duplicate concurrent requests carrying the same client-generated ID; tests that forget to release leases between cases.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- Related operations are being processed, please try again lat
- Please unlock the encrypted notebook first
- The doc in the user guide does not support sharing to the co
- result.Msg
- Please unlock the encrypted notebook first
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/4f845d89db0fc708.
Report an issue: GitHub.