{"record":{"id":"777c66e385b28b1a","repo":"koral--/android-gif-drawable","slug":"frame-index-is-not-positive","errorCode":null,"errorMessage":"Frame index is not positive","messagePattern":"Frame index is not positive","errorType":"exception","errorClass":"IndexOutOfBoundsException","httpStatus":null,"severity":"error","filePath":"android-gif-drawable/src/main/java/pl/droidsonroids/gif/GifDrawable.java","lineNumber":574,"sourceCode":"\t\t\tthrow new IllegalArgumentException(\"Position is not positive\");\n\t\t}\n\n\t\tsynchronized (mNativeInfoHandle) {\n\t\t\tmNativeInfoHandle.seekToTime(position, mBuffer);\n\t\t}\n\t\tmInvalidationHandler.sendEmptyMessageAtTime(MSG_TYPE_INVALIDATION, 0);\n\t}\n\n\t/**\n\t * Like {@link #seekTo(int)} but uses index of the frame instead of time.\n\t * If <code>frameIndex</code> exceeds number of frames, seek stops at the end, no exception is thrown.\n\t *\n\t * @param frameIndex index of the frame to seek to (zero based)\n\t * @throws IllegalArgumentException if <code>frameIndex</code>&lt;0\n\t */\n\tpublic void seekToFrame(@IntRange(from = 0, to = Integer.MAX_VALUE) final int frameIndex) {\n\t\tif (frameIndex < 0) {\n\t\t\tthrow new IndexOutOfBoundsException(\"Frame index is not positive\");\n\t\t}\n\t\tmExecutor.execute(new SafeRunnable(this) {\n\t\t\t@Override\n\t\t\tpublic void doWork() {\n\t\t\t\tmNativeInfoHandle.seekToFrame(frameIndex, mBuffer);\n\t\t\t\tmInvalidationHandler.sendEmptyMessageAtTime(MSG_TYPE_INVALIDATION, 0);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Like {@link #seekToFrame(int)} but performs operation synchronously and returns that frame.\n\t *\n\t * @param frameIndex index of the frame to seek to (zero based)\n\t * @return frame at desired index\n\t * @throws IndexOutOfBoundsException if frameIndex&lt;0\n\t */\n\tpublic Bitmap seekToFrameAndGet(@IntRange(from = 0, to = Integer.MAX_VALUE) final int frameIndex) {","sourceCodeStart":556,"sourceCodeEnd":592,"githubUrl":"https://github.com/koral--/android-gif-drawable/blob/26ff795f78b7fe8bbaf375a947b2bda95fc58e3d/android-gif-drawable/src/main/java/pl/droidsonroids/gif/GifDrawable.java#L556-L592","documentation":"seekToFrame validates the zero-based frame index is non-negative before scheduling an async seek. Because the check < 0 rejects only negatives, passing 0 is legal; a negative index throws IndexOutOfBoundsException with the message 'Frame index is not positive'.","triggerScenarios":"Calling seekToFrame() with a negative frame index, e.g. currentFrame - 1 when already at frame 0, or a sentinel -1 from saved state.","commonSituations":"'Previous frame' buttons decrementing past 0; loop arithmetic modulo bugs; deserializing state where -1 means 'no frame'.","solutions":["Clamp: Math.max(0, frameIndex) before calling","Wrap-around for previous-frame: (currentFrame - 1 + numberOfFrames) % numberOfFrames","Catch IndexOutOfBoundsException and call seekToFrame(0)"],"exampleFix":"// before\ngifDrawable.seekToFrame(currentFrame - 1);\n// after\ngifDrawable.seekToFrame(Math.max(0, currentFrame - 1));","handlingStrategy":"validation","validationCode":"if (frameIndex < 0) { frameIndex = 0; }","typeGuard":"int clampFrame(int idx, int frames) { return Math.max(0, Math.min(idx, frames - 1)); }","tryCatchPattern":"try { gifDrawable.seekToFrame(idx); } catch (IndexOutOfBoundsException e) { gifDrawable.seekToFrame(0); }","preventionTips":["Clamp frame indices to [0, getNumberOfFrames()-1]","Implement 'previous frame' with wrap-around modulo","Never use -1 as a frame sentinel when calling this API"],"tags":["android","gif","seek","frame-index"],"backgroundTag":"index-out-of-range","analyzedSha":"26ff795f78b7fe8bbaf375a947b2bda95fc58e3d","analyzedAt":"2026-09-10T14:37:11.595Z","contentChangedAt":"2026-09-10T14:37:11.595Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}