GopeedLab/gopeed · error

no parts found for %s

Error message

no parts found for %s

What it means

Returned by findZipMultiParts (extract_zip.go:59-86) when ZIP multi-part extraction finds no existing files matching the expected <base>.%03d sequence starting at .001. The caller strips the numeric suffix from firstPartPath, rebuilds Archive.zip.001, Archive.zip.002... via os.Stat, and a NotExist result on the very first part yields zero parts. It signals that the derived first-part filename does not exist on disk at extraction time.

Source

Thrown at pkg/download/extract_zip.go:82

	// Remove the .001 suffix to get the base
	if idx := strings.LastIndex(baseName, "."); idx > 0 {
		baseName = baseName[:idx] // "Archive.zip"
	}

	var parts []string
	partNum := 1

	for {
		partPath := filepath.Join(dir, baseName+fmt.Sprintf(".%03d", partNum))
		if _, err := os.Stat(partPath); os.IsNotExist(err) {
			break
		}
		parts = append(parts, partPath)
		partNum++
	}

	if len(parts) == 0 {
		return nil, fmt.Errorf("no parts found for %s", firstPartPath)
	}

	return parts, nil
}

// multiPartFileReader provides io.ReaderAt over multiple files concatenated
type multiPartFileReader struct {
	parts     []string
	files     []*os.File
	sizes     []int64
	offsets   []int64 // cumulative offsets for each file
	totalSize int64
}

func newMultiPartFileReader(parts []string) *multiPartFileReader {
	return &multiPartFileReader{parts: parts}
}

View on GitHub (pinned to 7b7327ffb3)

Solutions

  1. Verify the .001 first volume exists next to the parts you selected before triggering extraction
  2. Re-download or restore the missing first part so the full Archive.zip.001..NNN set is present in one directory
  3. Ensure part files keep the exact 3-digit suffix naming produced by the archiver
  4. If embedding, os.Stat the derived first part before calling extraction and surface a clearer error

Example fix

// before
err := extractMultiPartArchive("/data/Archive.zip.001", dest, "", nil)
// fails: /data/Archive.zip.001 was deleted after detection

// after
if _, err := os.Stat("/data/Archive.zip.001"); err != nil {
	return fmt.Errorf("multi-part zip incomplete, first part missing: %w", err)
}
err := extractMultiPartArchive("/data/Archive.zip.001", dest, "", nil)
Defensive patterns

Strategy: validation

Validate before calling

base := strings.TrimSuffix(firstPart, filepath.Ext(firstPart))
if _, err := os.Stat(base + ".001"); err != nil {
	return fmt.Errorf("multi-part zip incomplete: %s.001 not found", base)
}

Try / catch

if err := extractMultiPartArchive(p, dest, pw, cb); err != nil {
	if strings.Contains(err.Error(), "no parts found for") {
		log.Warn("first volume missing; re-download the .001 part")
	}
	return err
}

Prevention

When it happens

Trigger: The .001 part was deleted, moved or renamed after the archive was detected (auto-extract racing a user or cleanup job); the file set uses non-3-digit numbering (Archive.zip.1) that the %03d reconstruction never matches; the first part genuinely never downloaded; firstPartPath points into a directory that changed (relative path with a different process CWD).

Common situations: Post-download auto-extraction where a partial download only produced later-numbered parts; users manually deleting the first volume to save space; files renamed by browser dedupe (Archive (1).zip.001 breaking the base); NAS/docker deployments where the download directory is remapped between detection and extraction.

Related errors


AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16). Data as JSON: /api/errors/e19f813b5a96192e. Report an issue: GitHub.