{"id":"af4634c7e6efcef4","repo":"apache/kafka","slug":"timeout-of-ms-expired-before-the-position-for-pa-af4634","errorCode":null,"errorMessage":"Timeout of {}ms expired before the position for partition {} could be determined","messagePattern":"Timeout of (.+?)ms expired before the position for partition (.+?) could be determined","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java","lineNumber":889,"sourceCode":"\n    @Override\n    public long position(TopicPartition partition, final Duration timeout) {\n        acquireAndEnsureOpen();\n        try {\n            if (!this.subscriptions.isAssigned(partition))\n                throw new IllegalStateException(\"You can only check the position for partitions assigned to this consumer.\");\n\n            Timer timer = time.timer(timeout);\n            do {\n                SubscriptionState.FetchPosition position = this.subscriptions.validPosition(partition);\n                if (position != null)\n                    return position.offset;\n\n                updateFetchPositions(timer);\n                client.poll(timer);\n            } while (timer.notExpired());\n\n            throw new TimeoutException(\"Timeout of \" + timeout.toMillis() + \"ms expired before the position \" +\n                    \"for partition \" + partition + \" could be determined\");\n        } finally {\n            release();\n        }\n    }\n\n    @Override\n    public Map<TopicPartition, OffsetAndMetadata> committed(final Set<TopicPartition> partitions) {\n        return committed(partitions, Duration.ofMillis(defaultApiTimeoutMs));\n    }\n\n    @Override\n    public Map<TopicPartition, OffsetAndMetadata> committed(final Set<TopicPartition> partitions, final Duration timeout) {\n        acquireAndEnsureOpen();\n        long start = time.nanoseconds();\n        try {\n            throwIfGroupIdNotDefined();\n            final Map<TopicPartition, OffsetAndMetadata> offsets;","sourceCodeStart":871,"sourceCodeEnd":907,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java#L871-L907","documentation":"Thrown by KafkaConsumer.position(TopicPartition, Duration) when the timer expires before the consumer can resolve a valid fetch position (e.g. while waiting for offset reset, coordinator fetch, or metadata). The position is not yet known because the partition has not finished initialization within the supplied (or default) timeout. It indicates the broker/coordinator path was too slow or unreachable, not a logic error.","triggerScenarios":"Calling position() with a short Duration right after assign()/subscribe() with no committed offset and no offset-reset policy; broker slow to respond; consumer unable to reach the group coordinator; offset reset still in flight when the deadline lapses.","commonSituations":"First position() call in a freshly started consumer with default.api.timeout.ms too low for the environment; network latency or broker load; missing auto.offset.reset config on a topic with no committed offsets; coordinator leader election in progress.","solutions":["Pass a larger timeout to position(tp, Duration.ofSeconds(30)) or raise default.api.timeout.ms.","Ensure auto.offset.reset is set (earliest/latest) so a missing committed offset can be resolved quickly.","Verify broker reachability and group coordinator health; check for ongoing rebalances or coordinator failover.","Poll once before calling position() so initialization progresses before the timed call."],"exampleFix":"// before\nlong pos = consumer.position(tp); // uses default api timeout\n\n// after\nconsumer.poll(Duration.ofSeconds(2)); // let assignment + reset initialize\nlong pos = consumer.position(tp, Duration.ofSeconds(30));","handlingStrategy":"retry","validationCode":"// Nothing to validate pre-call; the failure is broker/coordinator latency.\n// Budget a generous timeout derived from your SLO, not the default:\nDuration posTimeout = Duration.ofMillis(Math.max(defaultApiTimeoutMs, 30_000));\nconsumer.position(tp, posTimeout);","typeGuard":null,"tryCatchPattern":"// Retry with exponential backoff and a per-attempt cap; position() is idempotent and safe to re-issue:\nDuration[] backoff = {Duration.ofMillis(500), Duration.ofMillis(2_000), Duration.ofMillis(10_000)};\nfor (int attempt = 0; attempt < backoff.length + 1; attempt++) {\n    try {\n        return consumer.position(tp, Duration.ofSeconds(30));\n    } catch (TimeoutException e) {\n        if (attempt == backoff.length) throw e;\n        Thread.sleep(backoff[attempt].toMillis());\n    }\n}\nthrow new IllegalStateException(\"unreachable\");","preventionTips":["Call consumer.position() only after the partition has had a chance to fetch its position (e.g. after the first poll() that returned records), not immediately after assign()/seek().","If you need positions for many partitions, batch via offsetsForTimes/beginningOffsets rather than N sequential position() calls that each burn the full timeout.","Watch broker latency and consumer fetch lag; a position() timeout is usually a symptom of an unhealthy cluster, not a code bug."],"tags":["consumer","timeout","network","offset-reset","java"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}