{"record":{"id":"609596c050fab284","repo":"apache/iceberg","slug":"noop-timer-has-no-duration","errorCode":null,"errorMessage":"NOOP timer has no duration","messagePattern":"NOOP timer has no duration","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/org/apache/iceberg/metrics/Timer.java","lineNumber":145,"sourceCode":"\n    Timed NOOP = () -> {};\n  }\n\n  Timer NOOP =\n      new Timer() {\n        @Override\n        public Timed start() {\n          return Timed.NOOP;\n        }\n\n        @Override\n        public long count() {\n          throw new UnsupportedOperationException(\"NOOP timer has no count\");\n        }\n\n        @Override\n        public Duration totalDuration() {\n          throw new UnsupportedOperationException(\"NOOP timer has no duration\");\n        }\n\n        @Override\n        public TimeUnit unit() {\n          throw new UnsupportedOperationException(\"NOOP timer has no unit\");\n        }\n\n        @Override\n        public void record(long amount, TimeUnit unit) {}\n\n        @Override\n        public void time(Runnable runnable) {}\n\n        @Override\n        public <T> T timeCallable(Callable<T> callable) throws Exception {\n          return callable.call();\n        }\n","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/api/src/main/java/org/apache/iceberg/metrics/Timer.java#L127-L163","documentation":"The NOOP Timer discards all timing data, so it has no total duration to expose. totalDuration() throws UnsupportedOperationException to make accidental metric reads on the disabled timer fail fast instead of silently returning zero. Callers must use a real timer implementation to read durations.","triggerScenarios":"Calling Timer.NOOP.totalDuration(), or reading totalDuration() from a timer obtained through a metrics-disabled code path (Timer.Timed.NOOP / NOOP metrics context).","commonSituations":"Logging scan/commit timing summaries in code that runs with NOOP metrics enabled; tests asserting on durations without a real metrics implementation; dashboards or commit summary producers consuming Timer results unconditionally.","solutions":["Instantiate a real timer (Timer.of / DefaultTimer) rather than Timer.NOOP wherever durations are read.","Check for the NOOP instance before reading: skip or substitute Duration.ZERO when timer == Timer.NOOP.","Wrap metric reads in try-catch for UnsupportedOperationException only if NOOP use is unavoidable in legacy paths."],"exampleFix":"// before\nDuration d = timer.totalDuration();\n// after\nDuration d = (timer == Timer.NOOP) ? Duration.ZERO : timer.totalDuration();","handlingStrategy":"type-guard","validationCode":"boolean hasDuration = timer != Timer.NOOP;","typeGuard":"if (timer == Timer.NOOP) { return Duration.ZERO; } return timer.totalDuration();","tryCatchPattern":"try { return timer.totalDuration(); } catch (UnsupportedOperationException e) { return Duration.ZERO; }","preventionTips":["Only read durations from concrete timer implementations","Skip NOOP timers when building commit/scan reports","Keep metrics reads behind a helper that null/NOOP-checks"],"tags":["java","metrics","unsupported-operation","noop"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}