benbjohnson/litestream · error

unsupported file control op: %d

Error message

unsupported file control op: %d

What it means

VFSFile.FileControl() in vfs.go was invoked with a file-control opcode other than SQLITE_FCNTL_PRAGMA (14). FileControl in this VFS only implements PRAGMA dispatch; any other xFileControl op (e.g. other SQLITE_FCNTL_* codes SQLite probes for) is rejected with this error instead of returning a graceful not-supported signal.

Source

Thrown at vfs.go:2429

	cfg := &dateparser.Configuration{
		CurrentTime: time.Now().UTC(),
	}
	result, err := dateparser.Parse(cfg, value)
	if err != nil {
		return time.Time{}, fmt.Errorf("invalid timestamp (expected RFC3339 or relative time like '5 minutes ago'): %s", value)
	}
	if result.Time.IsZero() {
		return time.Time{}, fmt.Errorf("could not parse time: %s", value)
	}
	return result.Time.UTC(), nil
}

// FileControl handles file control operations, specifically PRAGMA commands for time travel.
func (f *VFSFile) FileControl(op int, pragmaName string, pragmaValue *string) (*string, error) {
	const SQLITE_FCNTL_PRAGMA = 14

	if op != SQLITE_FCNTL_PRAGMA {
		return nil, fmt.Errorf("unsupported file control op: %d", op)
	}

	name := strings.ToLower(pragmaName)

	f.logger.Debug("file control", "pragma", name, "value", pragmaValue)

	switch name {
	case "litestream_txid":
		if pragmaValue != nil {
			return nil, fmt.Errorf("litestream_txid is read-only")
		}
		txid := f.Pos().TXID
		result := txid.String()
		return &result, nil

	case "litestream_lag":
		if pragmaValue != nil {
			return nil, fmt.Errorf("litestream_lag is read-only")

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Confirm which opcode value appears in the message; only 14 (SQLITE_FCNTL_PRAGMA) is supported
  2. If it comes from SQLite's internal probing, treat it as benign/not-applicable and ensure the caller handles the error as 'not supported'
  3. Route PRAGMA operations via PRAGMA statements so they arrive as op 14
  4. If a needed op is legitimately required (e.g. size hint or persist-WAL), extend FileControl to handle or gracefully ignore that opcode
  5. Check the litestream VFS version for added opcode support

Example fix

// before
result, err := file.Control(SOME_FCNTL_OP, "", nil)
if err != nil { panic(err) }
// after
result, err := file.Control(SOME_FCNTL_OP, "", nil)
if err != nil {
    if strings.Contains(err.Error(), "unsupported file control op") {
        return nil // op not supported by this VFS; proceed without it
    }
    return err
}
// or use PRAGMA instead:
db.Exec("PRAGMA litestream_txid")
Defensive patterns

Strategy: try-catch

Validate before calling

// Only issue op 14 (SQLITE_FCNTL_PRAGMA) through this VFS; prefer PRAGMA statements:
const SQLITE_FCNTL_PRAGMA = 14
if op != SQLITE_FCNTL_PRAGMA {
    // skip: this VFS only supports PRAGMA file controls
    return
}

Type guard

func isPragmaFileControl(op int) bool { return op == 14 }

Try / catch

_, err := file.FileControl(op, name, val)
if err != nil {
    var unsupported bool
    if strings.Contains(err.Error(), "unsupported file control op") {
        unsupported = true // treat as not-implemented, degrade gracefully
    }
    _ = unsupported
}

Prevention

When it happens

Trigger: SQLite core or an extension issues an xFileControl call with an opcode other than 14 against the litestream VFS file — typically during database open/feature probing, WAL mode negotiation, or when an extension requests an fcntl the VFS does not implement.

Common situations: Happens during normal SQLite operation when the core probes for optional file-control features; also seen when drivers or tools issue non-PRAGMA file controls directly. Developers debugging 'unsupported file control op' usually assumed all fcntl ops were handled or that the error indicates corruption — it does not.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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