{"record":{"id":"687d41669ae7a8b6","repo":"prestodb/presto","slug":"ids-length-is-less-than-positioncount","errorCode":null,"errorMessage":"ids length is less than positionCount","messagePattern":"ids length is less than positionCount","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"presto-common/src/main/java/com/facebook/presto/common/block/DictionaryBlock.java","lineNumber":106,"sourceCode":"    }\n\n    public DictionaryBlock(int positionCount, Block dictionary, int[] ids, boolean dictionaryIsCompacted, DictionaryId dictionarySourceId)\n    {\n        this(0, positionCount, dictionary, ids, dictionaryIsCompacted, dictionarySourceId);\n    }\n\n    public DictionaryBlock(int idsOffset, int positionCount, Block dictionary, int[] ids, boolean dictionaryIsCompacted, DictionaryId dictionarySourceId)\n    {\n        requireNonNull(dictionary, \"dictionary is null\");\n        requireNonNull(ids, \"ids is null\");\n\n        if (positionCount < 0) {\n            throw new IllegalArgumentException(\"positionCount is negative\");\n        }\n\n        this.idsOffset = idsOffset;\n        if (ids.length - idsOffset < positionCount) {\n            throw new IllegalArgumentException(\"ids length is less than positionCount\");\n        }\n\n        this.positionCount = positionCount;\n        this.dictionary = dictionary;\n        this.ids = ids;\n        this.dictionarySourceId = requireNonNull(dictionarySourceId, \"dictionarySourceId is null\");\n        this.retainedSizeInBytes = INSTANCE_SIZE + dictionary.getRetainedSizeInBytes() + sizeOf(ids);\n\n        if (dictionaryIsCompacted) {\n            this.sizeInBytes = dictionary.getSizeInBytes() + (Integer.BYTES * (long) positionCount);\n            this.uniqueIds = dictionary.getPositionCount();\n        }\n    }\n\n    @Override\n    public int getSliceLength(int position)\n    {\n        return dictionary.getSliceLength(getId(position));","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-common/src/main/java/com/facebook/presto/common/block/DictionaryBlock.java#L88-L124","documentation":"DictionaryBlock stores ids as an int array with an idsOffset window. The constructor requires that ids.length - idsOffset >= positionCount, i.e. the array must contain at least positionCount entries after the offset. Otherwise the block would read out of bounds, so IllegalArgumentException(\"ids length is less than positionCount\") is thrown.","triggerScenarios":"Constructing DictionaryBlock(idsOffset, positionCount, dictionary, ids, ...) where ids.length - idsOffset < positionCount — e.g. slicing an ids array with an offset but keeping the original full length as positionCount, or truncating ids without reducing positionCount.","commonSituations":"Region/slice code (getRegion, copyRegion) computing idsOffset and positionCount inconsistently; custom dictionary compaction that reuses a smaller ids array; deserialization mismatch between declared position count and actual payload size.","solutions":["Ensure ids.length - idsOffset >= positionCount before construction, or throw a clear upstream error","When slicing, compute positionCount from the sliced array length: ids.length - idsOffset","After compaction/compaction logic, rebuild ids (or resize) so it matches the declared positionCount","Check deserialization that the ids payload length matches the page header's position count"],"exampleFix":"// before\nint[] ids = fullIds; int idsOffset = 100; int positionCount = fullIds.length; // 100 + len > ids.length\nnew DictionaryBlock(idsOffset, positionCount, dictionary, ids, false, sourceId);\n// after\nint positionCount = fullIds.length - idsOffset; // consistent with the offset window\ncheckArgument(fullIds.length - idsOffset >= positionCount);\nnew DictionaryBlock(idsOffset, positionCount, dictionary, fullIds, false, sourceId);","handlingStrategy":"validation","validationCode":"static DictionaryBlock safeDictionaryBlock(int idsOffset, int positionCount, Block dictionary, int[] ids, boolean compacted, DictionaryId sourceId) {\n    checkArgument(ids != null && ids.length - idsOffset >= positionCount,\n        \"ids.length=%s idsOffset=%s positionCount=%s\", ids == null ? -1 : ids.length, idsOffset, positionCount);\n    return new DictionaryBlock(idsOffset, positionCount, dictionary, ids, compacted, sourceId);\n}","typeGuard":null,"tryCatchPattern":"try {\n    return new DictionaryBlock(idsOffset, positionCount, dictionary, ids, false, sourceId);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"ids length is less than positionCount\")) {\n        // rebuild ids from the dictionary, or fall back to the non-dictionary block\n        return dictionary.getLoadedBlock();\n    }\n    throw e;\n}","preventionTips":["Compute positionCount as ids.length - idsOffset when slicing","Keep idsOffset, ids array, and positionCount derived from the same source region","Validate deserialized ids payload size against the header position count","Add unit tests for region/slice paths that adjust idsOffset"],"tags":["block","dictionary","argument-validation","presto"],"backgroundTag":"negative-position-count","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}