koral--/android-gif-drawable · error · IllegalArgumentException

Position is not positive

Error message

Position is not positive

What it means

GifDrawable.seekTo validates the position in milliseconds is non-negative before scheduling an async seek on its executor. Negative positions are meaningless in a GIF timeline, so it throws IllegalArgumentException immediately on the calling thread.

Solutions

  1. Clamp before calling: Math.max(0, position)
  2. Guard the source: treat sentinel/negative values as 'restart from 0' by calling seekTo(0) or resetAnimation()
  3. Catch IllegalArgumentException around seekTo and fall back to seekTo(0)

Example fix

// before
gifDrawable.seekTo(positionMs);
// after
gifDrawable.seekTo(Math.max(0, positionMs));
Defensive patterns

Strategy: validation

Validate before calling

if (positionMs < 0) { positionMs = 0; }

Type guard

int clampPosition(int pos) { return Math.max(0, pos); }

Try / catch

try { gifDrawable.seekTo(pos); } catch (IllegalArgumentException e) { gifDrawable.seekTo(0); }

Prevention

When it happens

Trigger: Calling seekTo() with a negative value, e.g. computing position from currentTime - offset, or from an external player state that went negative.

Common situations: Custom media controls computing seek targets by subtraction; binding progress values that can be negative; restoring saved state with a sentinel -1 meaning 'unknown position'.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of koral--/android-gif-drawable@26ff795f78 (2026-09-10). Data as JSON: /api/errors/7370cd4ac8defa0c. Report an issue: GitHub.

Appendix: source

Thrown at android-gif-drawable/src/main/java/pl/droidsonroids/gif/GifDrawable.java:537

		return mNativeInfoHandle.getCurrentPosition();
	}

	/**
	 * Seeks animation to given absolute position (within given loop) and refreshes the canvas.<br>
	 * If <code>position</code> is greater than duration of the loop of animation (or whole animation if there is no loop)
	 * then animation will be sought to the end, no exception will be thrown.<br>
	 * NOTE: all frames from current (or first one if seeking backward) to desired one must be rendered sequentially to perform seeking.
	 * It may take a lot of time if number of such frames is large.
	 * Method is thread-safe. Decoding is performed in background thread and drawable is invalidated automatically
	 * afterwards.
	 *
	 * @param position position to seek to in milliseconds
	 * @throws IllegalArgumentException if <code>position</code>&lt;0
	 */
	@Override
	public void seekTo(@IntRange(from = 0, to = Integer.MAX_VALUE) final int position) {
		if (position < 0) {
			throw new IllegalArgumentException("Position is not positive");
		}
		mExecutor.execute(new SafeRunnable(this) {
			@Override
			public void doWork() {
				mNativeInfoHandle.seekToTime(position, mBuffer);
				mGifDrawable.mInvalidationHandler.sendEmptyMessageAtTime(MSG_TYPE_INVALIDATION, 0);
			}
		});
	}

	/**
	 * Like {@link #seekTo(int)} but performs operation synchronously on current thread
	 *
	 * @param position position to seek to in milliseconds
	 * @throws IllegalArgumentException if <code>position</code>&lt;0
	 */
	public void seekToBlocking(@IntRange(from = 0, to = Integer.MAX_VALUE) final int position) {
		if (position < 0) {

View on GitHub (pinned to 26ff795f78)