cloudreve/cloudreve · warning

failed to read info of %q: %s, skipping...

Error message

failed to read info of %q: %s, skipping...

What it means

Produced by the Eject walk when fs.WalkDir hands its callback an error for an entry while exporting embedded static resources to disk (application/statics/statics.go:177-180). It formats the cause with %s, so errors.Is/errors.As cannot see through it. Although the message says 'skipping...', the code returns the error, which stops the entire walk and fails Eject - the skip is not actually performed.

Source

Thrown at application/statics/statics.go:179

	})

	var embedFS FS
	embedFS.files = &files
	return embedFS
}

// Eject 抽离内置静态资源
func Eject(l logging.Logger, statics fs.FS) error {
	// 初始化静态资源
	embedFS, err := fs.Sub(statics, "assets/build")
	if err != nil {
		l.Panic("Failed to initialize static resources: %s", err)
	}

	var walk func(relPath string, d fs.DirEntry, err error) error
	walk = func(relPath string, d fs.DirEntry, err error) error {
		if err != nil {
			return fmt.Errorf("failed to read info of %q: %s, skipping...", relPath, err)
		}

		if !d.IsDir() {
			// 写入文件
			dst := util.DataPath(filepath.Join(StaticFolder, relPath))
			out, err := util.CreatNestedFile(dst)
			defer out.Close()

			if err != nil {
				return fmt.Errorf("failed to create file %q: %s, skipping...", dst, err)
			}

			l.Info("Ejecting %q...", dst)
			obj, _ := embedFS.Open(relPath)
			if _, err := io.Copy(out, bufio.NewReader(obj)); err != nil {
				return fmt.Errorf("cannot write file %q: %s, skipping...", relPath, err)
			}
		}

View on GitHub (pinned to 20c95ad73f)

Solutions

  1. Treat it as a broken embedded archive: rebuild the binary with a freshly built assets.zip.
  2. Validate the embedded archive independently (unzip -t on the source zip used at build time).
  3. If you want per-file resilience, change the handler to log and return nil so the walk really skips the entry, matching the message.
  4. Note the %s formatting: to inspect the cause programmatically, change it to %w first.

Example fix

// before
if err != nil {
	return fmt.Errorf("failed to read info of %q: %s, skipping...", relPath, err)
}

// after - honor the "skipping" intent and keep the error chain
if err != nil {
	l.Warning("failed to read info of %q, skipping: %s", relPath, err)
	return nil
}
Defensive patterns

Strategy: try-catch

Try / catch

// The message says "skipping..." but the shipped code returns the error,
// aborting the whole eject. Honor the intent:
if err != nil {
	l.Warning("failed to read info of %q, skipping: %s", relPath, err)
	return nil // continue walking siblings
}
// Note: shipped code uses %s, so errors.Is/As cannot see the cause.

Prevention

When it happens

Trigger: Reading a directory entry from the in-memory FS fails (corrupt embedded zip, same class as errors 115-117); d.Info() failing for an entry; memory pressure while enumerating a large archive. Because the FS is the already-materialized in-memory one, occurrences indicate a broken embedded archive rather than disk trouble.

Common situations: Running the eject command on a binary whose assets.zip is subtly corrupt; forks with modified asset pipelines producing odd entries.

Related errors


AI-assisted analysis of cloudreve/cloudreve@20c95ad73f (2026-08-16). Data as JSON: /api/errors/8d871f3035b90f9b. Report an issue: GitHub.