AlistGo/alist · error

[doubao_new] short read: seq=%d want=%d got=%d

Error message

[doubao_new] short read: seq=%d want=%d got=%d

What it means

Doubao_new spool-read failure: tmpFile.ReadAt for a needed block returned fewer bytes (n) than the server-declared item.Size. The block lives at offset seq*blockSize in the local temp spool file, and the spool is shorter or sparser than expected at that offset.

Source

Thrown at drivers/doubao_new/driver.go:356

			}
			return nil
		}

		for _, item := range needed.NeededUploadBlocks {
			if _, ok := blockMeta[item.Seq]; !ok {
				return nil, fmt.Errorf("[doubao_new] missing block meta for seq %d", item.Seq)
			}
			if item.Size <= 0 {
				return nil, fmt.Errorf("[doubao_new] invalid block size from needed list: seq=%d size=%d", item.Seq, item.Size)
			}
			offset := int64(item.Seq) * blockSize
			buf := make([]byte, int(item.Size))
			n, err := tmpFile.ReadAt(buf, offset)
			if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
				return nil, err
			}
			if n != len(buf) {
				return nil, fmt.Errorf("[doubao_new] short read: seq=%d want=%d got=%d", item.Seq, len(buf), n)
			}
			buf = buf[:n]
			realAdler := adler32String(buf)
			if realAdler != item.Checksum {
				return nil, fmt.Errorf("[doubao_new] block checksum mismatch: seq=%d offset=%d adler32=%s step2=%s", item.Seq, offset, realAdler, item.Checksum)
			}
			payloadStart := groupBuf.Len()
			groupBuf.Write(buf)
			payloadEnd := groupBuf.Len()
			payloadAdler := adler32String(groupBuf.Bytes()[payloadStart:payloadEnd])
			if payloadAdler != item.Checksum {
				return nil, fmt.Errorf("[doubao_new] payload checksum mismatch: seq=%d start=%d end=%d adler32=%s step2=%s", item.Seq, payloadStart, payloadEnd, payloadAdler, item.Checksum)
			}
			groupSeqs = append(groupSeqs, item.Seq)
			groupChecksums = append(groupChecksums, item.Checksum)
			groupSizes = append(groupSizes, item.Size)
			groupRealSize += int64(n)
			groupExpectSum += item.Size

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Check free space on the volume hosting the temp/spool directory; free space and retry
  2. Retry the upload — spool is regenerated each attempt
  3. Move the OpenList temp directory to a volume with adequate space for the full file
  4. Verify the source file's size hasn't changed since upload start
  5. If the last seq always short-reads, report the final-block sizing arithmetic upstream
Defensive patterns

Strategy: validation

Validate before calling

// before reading blocks, confirm the spool covers the highest offset
fi, err := tmpFile.Stat()
if err != nil { return err }
maxOff := int64(maxNeededSeq)*blockSize + maxNeededSize
if fi.Size() < maxOff {
    return fmt.Errorf("spool too short: have=%d need=%d", fi.Size(), maxOff)
}

Try / catch

if err := readBlock(...); err != nil && strings.Contains(err.Error(), "short read") {
    return d.uploadPass(ctx, ...) // regenerate the spool from scratch
}

Prevention

When it happens

Trigger: ReadAt at offset seq*blockSize hits EOF early because the temp file was truncated after being written (disk full, cleanup race), the spool write earlier silently wrote less than blockSize, or size/offset arithmetic overestimates the final block.

Common situations: Disk exhaustion in the temp directory mid-upload, /tmp cleaners deleting or truncating spool files on long uploads, or driver changes to blockSize without regenerating spool expectations.

Related errors


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/7aedf97437026270. Report an issue: GitHub.