AlistGo/alist · error

the number of exposed ports and listened ports does not matc

Error message

the number of exposed ports and listened ports does not match

What it means

Returned while parsing an exposed:listened passive-port mapping entry (form 'exposedRange:listenRange', for NAT/container port remapping). Each side is parsed into (start, length); the mapper requires both ranges to cover the same number of ports so the random index into the pool maps 1:1. As with other mapper parse errors, the entry is fatal-logged and the mapper function is discarded.

Source

Thrown at server/ftp.go:237

			}
		}
	}
	for i, mapper := range pasvPortMappers {
		var err error
		exposed, listened, mapped := strings.Cut(mapper, ":")
		for {
			if mapped {
				var es, ls, el, ll int
				es, el, err = convertToPorts(exposed)
				if err != nil {
					break
				}
				ls, ll, err = convertToPorts(listened)
				if err != nil {
					break
				}
				if el != ll {
					err = errors.New("the number of exposed ports and listened ports does not match")
					break
				}
				groups[i].ExposedStart = es
				groups[i].ListenedStart = ls
				groups[i].Length = el
				totalLength += el
			} else {
				var start, length int
				start, length, err = convertToPorts(mapper)
				groups[i].ExposedStart = start
				groups[i].ListenedStart = start
				groups[i].Length = length
				totalLength += length
			}
			break
		}
		if err != nil {
			utils.Log.Fatalf("failed to convert FTP PASV port mapper %s: %v, the port mapper will be ignored.", mapper, err)

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Make both ranges the same length, e.g. '50000-50100:60000-60100'
  2. If ports map 1:1 identically, drop the colon form and just use the plain range '50000-50100'
  3. Restart AList and confirm no 'port mapper will be ignored' fatal log appears

Example fix

// before
pasv: "50000-50100:60000-60200"
// after
pasv: "50000-50100:60000-60100"
Defensive patterns

Strategy: validation

Validate before calling

func validMapping(entry string) bool {
    exposed, listened, mapped := strings.Cut(entry, ":")
    if !mapped { return true }
    _, el := parseRangeLen(exposed)
    _, ll := parseRangeLen(listened)
    return el > 0 && el == ll
}

Prevention

When it happens

Trigger: A setting like '50000-50100:60000-60200' where the exposed range spans 101 ports but the listened range spans 201; any exposed:listed pair whose range lengths differ.

Common situations: Docker/NAT deployments where the host-published range and container-listening range were configured with different widths; editing one side of the mapping and forgetting the other; mixing single-port and range syntax between the two halves.

Related errors


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