{"record":{"id":"3a9de198374deb42","repo":"AlistGo/alist","slug":"istruncated-nil","errorCode":null,"errorMessage":"IsTruncated nil","messagePattern":"IsTruncated nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/s3/util.go","lineNumber":135,"sourceCode":"\t\t}\n\t\tfor _, object := range listObjectsResult.Contents {\n\t\t\tif strings.HasSuffix(*object.Key, \"/\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tname := path.Base(*object.Key)\n\t\t\tif !args.S3ShowPlaceholder && (name == getPlaceholderName(d.Placeholder) || name == d.Placeholder) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfile := &model.Object{\n\t\t\t\t//Id:        *object.Key,\n\t\t\t\tName:     name,\n\t\t\t\tSize:     *object.Size,\n\t\t\t\tModified: *object.LastModified,\n\t\t\t}\n\t\t\tfiles = append(files, model.WrapObjStorageClass(file, aws.StringValue(object.StorageClass)))\n\t\t}\n\t\tif listObjectsResult.IsTruncated == nil {\n\t\t\treturn nil, errors.New(\"IsTruncated nil\")\n\t\t}\n\t\tif *listObjectsResult.IsTruncated {\n\t\t\tmarker = *listObjectsResult.NextMarker\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn files, nil\n}\n\nfunc (d *S3) listV2(prefix string, args model.ListArgs) ([]model.Obj, error) {\n\tprefix = getKey(prefix, true)\n\tfiles := make([]model.Obj, 0)\n\tvar continuationToken, startAfter *string\n\tfor {\n\t\tinput := &s3.ListObjectsV2Input{\n\t\t\tBucket:            &d.Bucket,\n\t\t\tContinuationToken: continuationToken,","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/s3/util.go#L117-L153","documentation":"Thrown by the S3 driver's legacy ListObjects V1 pagination loop after a successful ListObjects call whose response body omitted the IsTruncated field. The AWS SDK models IsTruncated as a *bool, so a provider that simply does not return it yields nil, and the driver aborts instead of guessing whether more pages exist. It is a defect/quirk of S3-compatible providers rather than of real AWS S3, which always sets the field.","triggerScenarios":"Listing a bucket with list_version=v1 against an S3-compatible endpoint (some MinIO/Ceph/vendor gateways) that returns <ListBucketResult> without <IsTruncated>. The very first page (or any later page) then triggers this error before pagination can continue.","commonSituations":"Self-hosted MinIO or Ceph RGW endpoints, NAS vendor 'S3' services, or older gateway firmware that implement the V1 API incompletely; also misconfigured custom endpoint_url pointing at such a gateway. Real AWS, and list_version v2 (ListObjectsV2 uses different pagination fields), never hit it.","solutions":["Switch the storage config to list_version = v2 (ListObjectsV2), which uses ContinuationToken and does not depend on IsTruncated","Upgrade the S3-compatible gateway (MinIO/Ceph) to a version that fully populates IsTruncated in V1 responses","Patch listV1 to treat a nil IsTruncated as 'not truncated' via aws.BoolValue(listObjectsResult.IsTruncated) when the result set is smaller than the page size"],"exampleFix":"// before\nif listObjectsResult.IsTruncated == nil {\n\treturn nil, errors.New(\"IsTruncated nil\")\n}\nif *listObjectsResult.IsTruncated {\n\tmarker = *listObjectsResult.NextMarker\n} else {\n\tbreak\n}\n\n// after (defensive: nil treated as end of listing)\nif aws.BoolValue(listObjectsResult.IsTruncated) && listObjectsResult.NextMarker != nil {\n\tmarker = *listObjectsResult.NextMarker\n} else {\n\tbreak\n}","handlingStrategy":"validation","validationCode":"// before switching to list v1, probe the provider\nout, err := s3client.ListObjects(&s3.ListObjectsInput{Bucket: aws.String(bucket), MaxKeys: aws.Int64(1)})\nif err != nil { return err }\nif out.IsTruncated == nil {\n    // provider omits IsTruncated: use ListObjectsV2 for this storage\n    return configureListVersion(\"v2\")\n}","typeGuard":"func hasTruncationFlag(out *s3.ListObjectsOutput) bool {\n    return out != nil && out.IsTruncated != nil\n}","tryCatchPattern":"// in a wrapper around List\nfiles, err := d.List(ctx, dir, args)\nif err != nil && strings.Contains(err.Error(), \"IsTruncated nil\") {\n    log.Warn(\"provider omits IsTruncated; switching storage to list_version=v2\")\n    setStorageListVersion(\"v2\") // next List call succeeds\n    files, err = d.List(ctx, dir, args)\n}","preventionTips":["Prefer list_version=v2 for any non-AWS S3 endpoint","Test new S3-compatible endpoints with a one-object listing before mounting large buckets","Patch listV1 defensively with aws.BoolValue so nil means 'done'"],"tags":["s3","pagination","aws-sdk","s3-compatible","minio"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}