siyuan-note/siyuan · error

Backup failed

Error message

Backup failed: %s

What it means

This is the generic failure branch of UploadCloudSnapshot: after UploadTagIndex fails with any error that is not ErrCloudBackupCountExceeded, the kernel calls handleCloudError (which may surface account/network diagnostics), then wraps the cause with formatRepoErrorMsg into the localized 'Backup failed: %s' template (Language 84). The %s payload is a humanized repo error, so this message is an envelope whose real cause is embedded in the suffix.

Solutions

  1. Read the %s suffix in the error to identify the underlying repo error and fix that cause first
  2. Verify the cloud account is logged in (Settings - Account) and the subscription is active; re-login to refresh the token
  3. Check network connectivity/proxy settings and retry, since uploads resume incrementally
  4. Inspect kernel logs for the handleCloudError diagnostics (e.g. 401/403/5xx details) for the precise failure
  5. If the error persists for one snapshot, create a fresh snapshot tag and upload that instead

Example fix

// before
if err != nil {
    return fmt.Errorf("backup failed")
}
// after
if err != nil {
    if errors.Is(err, dejavu.ErrCloudBackupCountExceeded) {
        return fmt.Errorf(Conf.Language(84), Conf.Language(154))
    }
    handleCloudError(err)
    return fmt.Errorf(Conf.Language(84), formatRepoErrorMsg(err))
}
Defensive patterns

Strategy: retry

Validate before calling

// pre-check reachability and login before a long upload
const me = await fetchPost('/api/account/info', {});
if (!me.data || me.code !== 0) throw new Error('Not logged in to SiYuan Cloud');

Try / catch

try {
    await fetchPost('/api/repo/uploadCloudSnapshot', {tag, id});
} catch (e) {
    // parse the '%s' suffix of 'Backup failed: %s' for the repo error cause
    console.error('cloud backup cause:', e.msg);
    if (isTransient(e)) await sleep(retryBackoff); // retry network failures only
}

Prevention

When it happens

Trigger: UploadTagIndex inside UploadCloudSnapshot fails for reasons other than the 12-snapshot quota: network failure mid-upload, cloud authentication failure (invalid/changed token), corrupted local index, or cloud-side 5xx responses.

Common situations: Uploading a large snapshot over an unstable connection; SiYuan Cloud session token expired after password change; cloud service maintenance returning HTTP errors; local repo key mismatch after restoring config from another machine.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/38d0103849bd130c. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/repository.go:1549

			return
		}
	case conf.ProviderWebDAV, conf.ProviderS3, conf.ProviderLocal:
		if !IsPaidUser() {
			util.PushErrMsg(Conf.Language(214), 5000)
			return
		}
	}

	util.PushEndlessProgress(Conf.Language(116))
	defer util.PushClearProgress()
	uploadFileCount, uploadChunkCount, uploadBytes, err := repo.UploadTagIndex(tag, id, map[string]any{eventbus.CtxPushMsg: eventbus.CtxPushMsgToStatusBarAndProgress})
	if err != nil {
		if errors.Is(err, dejavu.ErrCloudBackupCountExceeded) {
			err = fmt.Errorf(Conf.Language(84), Conf.Language(154))
			return
		}
		handleCloudError(err)
		err = fmt.Errorf(Conf.Language(84), formatRepoErrorMsg(err))
		return
	}
	msg := fmt.Sprintf(Conf.Language(152), uploadFileCount, uploadChunkCount, humanize.BytesCustomCeil(uint64(uploadBytes), 2))
	util.PushMsg(msg, 5000)
	util.PushStatusBar(msg)
	return
}

func RemoveCloudRepoTag(tag string) (err error) {
	assetDownloadSourceMu.RLock()
	defer assetDownloadSourceMu.RUnlock()
	if 1 > len(Conf.Repo.Key) {
		err = errors.New(Conf.Language(26))
		return
	}

	handleCloudError := cloudRepoErrorHandler()
	defer func() { handleCloudError(err) }()

View on GitHub (pinned to 9f775e8a12)