benbjohnson/litestream · error

litestream_hydration_progress is read-only

Error message

litestream_hydration_progress is read-only

What it means

The litestream_hydration_progress PRAGMA is read-only: FileControl() in vfs.go returns this error when pragmaValue is non-nil (assignment form). Reading it returns the hydrator's completion percentage (0 when no hydrator is attached, otherwise hydrator.Status().Pct()*100 formatted to one decimal).

Source

Thrown at vfs.go:2482

		if strings.EqualFold(*pragmaValue, "latest") {
			if err := f.ResetTime(context.Background()); err != nil {
				return nil, err
			}
			return nil, nil
		}

		t, err := parseTimeValue(*pragmaValue)
		if err != nil {
			return nil, err
		}
		if err := f.SetTargetTime(context.Background(), t); err != nil {
			return nil, err
		}
		return nil, nil

	case "litestream_hydration_progress":
		if pragmaValue != nil {
			return nil, fmt.Errorf("litestream_hydration_progress is read-only")
		}
		if f.hydrator == nil {
			result := "0"
			return &result, nil
		}
		pct := f.hydrator.Status().Pct() * 100
		result := strconv.FormatFloat(pct, 'f', 1, 64)
		return &result, nil

	case "litestream_hydration_file":
		if pragmaValue != nil {
			return nil, fmt.Errorf("litestream_hydration_file is read-only")
		}
		result := f.hydrationPath
		return &result, nil

	case "litestream_write_enabled":
		if pragmaValue == nil {

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Remove the assignment; use the read form `PRAGMA litestream_hydration_progress;`
  2. Poll the read form until it reports 100.0 to wait for hydration completion
  3. If hydration appears stuck, inspect replica connectivity/storage rather than attempting to write progress
  4. Catch the error and explain progress is computed from hydrator status

Example fix

// before
PRAGMA litestream_hydration_progress = 100;  -- force-complete hydration
// after
PRAGMA litestream_hydration_progress;  -- read current % (e.g. '42.5')
-- poll until '100.0' to know hydration finished
Defensive patterns

Strategy: validation

Validate before calling

// Reject assignment form before executing
if strings.Contains(strings.ToLower(stmt), "litestream_hydration_progress=") ||
   strings.Contains(strings.ToLower(stmt), "litestream_hydration_progress =") {
    return errors.New("litestream_hydration_progress is read-only; use the read form PRAGMA litestream_hydration_progress;")
}

Try / catch

_, err := db.Exec("PRAGMA litestream_hydration_progress = ?", v)
if err != nil && strings.Contains(err.Error(), "litestream_hydration_progress is read-only") {
    // fall back to polling the read form
    row := db.QueryRow("PRAGMA litestream_hydration_progress")
    var pct string
    _ = row.Scan(&pct)
}

Prevention

When it happens

Trigger: Executing `PRAGMA litestream_hydration_progress = <value>` on a connection backed by the litestream VFS. The non-nil pragmaValue triggers the read-only guard.

Common situations: Developers try to 'set' hydration progress to mark hydration complete or reset it. Progress is a computed status of the internal hydrator; it can only be observed, and it advances as the hydrator downloads/applies LTX data.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/5dfb7f539a01dc4a. Report an issue: GitHub.