siyuan-note/siyuan · error

update channel is invalid

Error message

update channel is invalid

What it means

Returned by getUpdateRelease(force) when Conf.System.UpdateChannel is neither UpdateChannelStable nor a value accepted by isValidUpdateChannel. This is the read-path counterpart to error 940: it guards every release lookup. Unlike SetUpdateChannel (which is user-driven), this fires when the in-memory config holds a corrupted channel string.

Source

Thrown at kernel/model/updater_release.go:87

	Digest             string `json:"digest"`
	State              string `json:"state"`
}

var (
	cachedGitHubReleases    []*githubRelease
	githubReleasesCacheTime int64
	githubReleasesLock      sync.RWMutex
	githubReleasesFlight    singleflight.Group
	githubManifestCache     sync.Map
)

func getUpdateRelease(force bool) (*updateRelease, error) {
	channel := Conf.System.UpdateChannel
	if conf.UpdateChannelStable == channel {
		return getStableUpdateRelease(force)
	}
	if !isValidUpdateChannel(channel) {
		return nil, errors.New("update channel is invalid")
	}
	return getGitHubUpdateRelease(channel, force)
}

func getStableUpdateRelease(force bool) (*updateRelease, error) {
	result, err := util.GetRhyResult(context.TODO(), force)
	if err != nil {
		return nil, err
	}
	version, ok := result["ver"].(string)
	normalizedVersion := normalizeReleaseVersion(version)
	if !ok || !semver.IsValid(normalizedVersion) || "" != semver.Prerelease(normalizedVersion) {
		return nil, errors.New("stable release version is invalid")
	}
	version = strings.TrimPrefix(normalizedVersion, "v")

	release := &updateRelease{
		Version:    version,

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Reset the channel via the settings UI or by calling SetUpdateChannel(conf.UpdateChannelStable).
  2. Fix the conf.json file in the workspace so system.updateChannel is exactly "stable", "beta", or "alpha".
  3. Call loadGlobalUpdateChannel() to fall back to the per-user ~/.config/siyuan/update.json, which self-heals invalid values to stable (see lines 50-52).
Defensive patterns

Strategy: validation

Validate before calling

// Validate the in-memory channel before any release lookup.
if !isValidUpdateChannel(Conf.System.UpdateChannel) {
    Conf.System.UpdateChannel = conf.UpdateChannelStable // self-heal
}

Try / catch

// Fall back to stable on the read path.
release, err := getUpdateRelease(false)
if err != nil && strings.Contains(err.Error(), "update channel is invalid") {
    Conf.System.UpdateChannel = conf.UpdateChannelStable
    release, err = getUpdateRelease(false)
}

Prevention

When it happens

Trigger: Conf.System.UpdateChannel was set to an invalid value by direct struct mutation bypassing SetUpdateChannel. A config file (conf.json) was hand-edited with a bad channel and loaded at boot. A future code path assigns the channel without validation.

Common situations: Manually editing the workspace conf.json and misspelling the channel. A plugin writes to Conf.System directly. Migration/upgrade code that populated the field incorrectly.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/259560d9496f1f75. Report an issue: GitHub.