Bug WikiLogic & Control Flow BugsArray index out of bounds
6 examples

Array index out of bounds

Program attempts to access an invalid array index.

[ FAQ1 ]

What does array index out of bounds mean?

An array index out of bounds error means your program is referencing an invalid position within an array. Arrays have fixed sizes, defined at the time of their creation. In languages such as Java and C++, valid indices range from 0 to length - 1. Attempting to access an index outside this range (for instance, index -1 or index equal to the array length) triggers this error. This usually results in immediate termination of the program or unpredictable behavior, potentially compromising application stability.
[ FAQ2 ]

How to fix array index out of bounds

To fix an array index out of bounds error, ensure your program only accesses valid indices within the array. In languages like Java and C++, use conditional checks (if statements or loops) to verify that indices are within the valid range (0 to array.length - 1). Also, make sure loops iterate correctly, typically from 0 up to array.length - 1. Employing debugging tools or logging mechanisms helps quickly identify problematic indices. Additionally, code reviews and writing unit tests can proactively prevent this issue by catching boundary errors early in development.
diff block
+// components/warmup/WarmupItem.tsx
+import { List, Icon } from "@raycast/api";
+import { WarmupSet } from "../../types/warmup";
+import { formatWeight } from "../../utils/formatting";
+import { WARMUP_SCHEMES } from "../../constants/warmup";
+import { DetailView } from "./DetailView";
+import { ItemActions } from "./ItemActions";
+
+interface WarmupItemProps {
+ set: WarmupSet;
+ unitSystem: "kg" | "lbs";
+ sets: WarmupSet[]; // Add this prop
+ setShowingDetail: React.Dispatch<React.SetStateAction<boolean>>;
+}
+
+export const WarmupItem: React.FC<WarmupItemProps> = ({
+ set,
+ unitSystem,
+ sets, // Destructure the new prop
+ setShowingDetail,
+}) => {
+ const { color } = WARMUP_SCHEMES[set.setNumber - 1];
Greptile
greptile
logic: Potential array index out of bounds if set.setNumber is 0 or greater than WARMUP_SCHEMES.length
diff block
+import { Decoration, DecorationSet } from '@tiptap/pm/view'
+import { EditorState, Plugin, PluginKey } from 'prosemirror-state'
+import { EditorView } from 'prosemirror-view'
+import { BlockNoteEditor } from '../../BlockNoteEditor'
+import { BaseUiElementState } from '../../shared/BaseUiElementTypes'
+import { EventEmitter } from '../../shared/EventEmitter'
+import { BlockSchema } from '../Blocks/api/blockTypes'
+import { findBlock } from '../Blocks/helpers/findBlock'
+import { LinkMenuItem } from './LinkMenuItem'
+
+export const linkMenuPluginKey = new PluginKey('LinkMenuPlugin')
+
+export type LinkMenuState<LinkMenuItem> = BaseUiElementState & {
+ // The items to display.
+ items: LinkMenuItem[]
+ // Pasted URL
+ link: string
+ // The index of the suggested item that's currently hovered by the keyboard.
+ keyboardHoveredItemIndex: number
+}
+
+type LinkPluginState<LinkMenuItem> = {
+ // True when the menu is shown, false when hidden.
+ active: boolean
+ // Pasted URL
+ link: string
+ // The items that should be shown in the menu.
+ items: LinkMenuItem[]
+ // The index of the item in the menu that's currently hovered using the keyboard.
+ keyboardHoveredItemIndex: number | undefined
+ decorationId: string | undefined
+}
+
+function getDefaultPluginState<LinkMenuItem>(): LinkPluginState<LinkMenuItem> {
+ return {
+ active: false,
+ link: '',
+ items: [] as LinkMenuItem[],
+ keyboardHoveredItemIndex: undefined,
+ decorationId: undefined,
+ }
+}
+
+export class LinkMenuView<LinkMenuItem, BSchema extends BlockSchema> {
+ private linkMenuState?: LinkMenuState<LinkMenuItem>
+ public updateLinkMenu: () => void
+ pluginState: LinkPluginState<LinkMenuItem>
+
+ constructor(
+ private readonly editor: BlockNoteEditor<BSchema>,
+ private readonly pluginKey: PluginKey,
+ // private readonly pmView: EditorView,
+ updateLinkMenu: (linkMenuState: LinkMenuState<LinkMenuItem>) => void = () => {
+ // noop
+ },
+ ) {
+ this.pluginState = getDefaultPluginState<LinkMenuItem>()
+
+ this.updateLinkMenu = () => {
+ if (!this.linkMenuState) {
+ throw new Error('Attempting to update uninitialized Link menu')
+ }
+
+ updateLinkMenu(this.linkMenuState)
+ }
+
+ document.addEventListener('scroll', this.handleScroll)
+ }
+
+ handleScroll = () => {
+ if (this.linkMenuState?.show) {
+ const decorationNode = document.querySelector(`[data-decoration-id="${this.pluginState.decorationId}"]`)
+ this.linkMenuState.referencePos = decorationNode!.getBoundingClientRect()
+ this.updateLinkMenu()
+ }
+ }
+
+ update(view: EditorView, prevState: EditorState) {
+ const prev = this.pluginKey.getState(prevState)
+ const next = this.pluginKey.getState(view.state)
+
+ // See how the state changed
+ const started = !prev.active && next.active
+ const stopped = prev.active && !next.active
+ // TODO: Currently also true for cases in which an update isn't needed so selected list item index updates still
+ // cause the view to update. May need to be more strict.
+ const changed = prev.active && next.active
+
+ // Cancel when link isn't active
+ if (!started && !changed && !stopped) {
+ return
+ }
+
+ this.pluginState = stopped ? prev : next
+
+ if (stopped || !this.editor.isEditable) {
+ this.linkMenuState!.show = false
+ this.updateLinkMenu()
+
+ return
+ }
+
+ const decorationNode = document.querySelector(`[data-decoration-id="${this.pluginState.decorationId}"]`)
+
+ if (this.editor.isEditable) {
+ this.linkMenuState = {
+ show: true,
+ link: this.pluginState.link,
+ referencePos: decorationNode!.getBoundingClientRect(),
+ items: this.pluginState.items,
+ keyboardHoveredItemIndex: this.pluginState.keyboardHoveredItemIndex!,
+ }
+
+ this.updateLinkMenu()
+ }
+ }
+
+ destroy() {
+ document.removeEventListener('scroll', this.handleScroll)
+ }
+}
+
+export class LinkMenuProsemirrorPlugin<
+ BSchema extends BlockSchema,
+ MenuItem extends LinkMenuItem<BSchema>,
+> extends EventEmitter<any> {
+ // private linkMenuView: LinkMenuView<BSchema> | undefined
+ public readonly plugin: Plugin
+ public readonly itemCallback: (item: MenuItem, link: string) => void
+
+ constructor(editor: BlockNoteEditor<BSchema>) {
+ super()
+ const links = setupLinkMenu<MenuItem, BSchema>(
+ editor,
+ (state) => {
+ this.emit('update', state)
+ },
+ linkMenuPluginKey,
+ )
+ this.plugin = links.plugin
+ this.itemCallback = links.itemCallback
+ }
+
+ public onUpdate(callback: (state: LinkMenuState<BSchema>) => void) {
+ return this.on('update', callback)
+ }
+}
+
+export const setupLinkMenu = <MenuItem extends LinkMenuItem<BSchema>, BSchema extends BlockSchema>(
+ editor: BlockNoteEditor<BSchema>,
+ updateLinkMenu: (linkMenuState: LinkMenuState<MenuItem>) => void,
+
+ pluginKey: PluginKey,
+) => {
+ let linkPluginView: LinkMenuView<MenuItem, BSchema>
+
+ const deactivate = (view: EditorView) => {
+ view.dispatch(view.state.tr.setMeta(pluginKey, { deactivate: true }))
+ }
+
+ return {
+ plugin: new Plugin({
+ key: pluginKey,
+
+ view: () => {
+ linkPluginView = new LinkMenuView<MenuItem, BSchema>(
+ editor,
+ pluginKey,
+
+ updateLinkMenu,
+ )
+ return linkPluginView
+ },
+
+ state: {
+ // Initialize the plugin's internal state.
+ init(): LinkPluginState<MenuItem> {
+ return getDefaultPluginState<MenuItem>()
+ },
+
+ // Apply changes to the plugin state from an editor transaction.
+ apply(transaction, prev, oldState, newState): LinkPluginState<MenuItem> {
+ // TODO: More clearly define which transactions should be ignored.
+ if (transaction.getMeta('orderedListIndexing') !== undefined) {
+ return prev
+ }
+
+ const items = transaction.getMeta(pluginKey)?.items
+ const link = transaction.getMeta(pluginKey)?.link
+
+ // Checks if the menu should be shown.
+ if (transaction.getMeta(pluginKey)?.activate) {
+ return {
+ active: true,
+ link: link,
+ items: items,
+ keyboardHoveredItemIndex: 0,
+ decorationId: `id_${Math.floor(Math.random() * 0xffffffff)}`,
+ }
+ }
+
+ // Checks if the menu is hidden, in which case it doesn't need to be hidden or updated.
+ if (!prev.active) {
+ return prev
+ }
+
+ const next = { ...prev }
+ if (items) next.items = items
+ if (link) next.link = link
+
+ // Hides the menu
+ if (
+ // Highlighting text should hide the menu.
+ newState.selection.from !== newState.selection.to ||
+ // Transactions with plugin metadata {deactivate: true} should hide the menu.
+ transaction.getMeta(pluginKey)?.deactivate ||
+ // Certain mouse events should hide the menu.
+ // TODO: Change to global mousedown listener.
+ transaction.getMeta('focus') ||
+ transaction.getMeta('blur') ||
+ transaction.getMeta('pointer')
+ ) {
+ return getDefaultPluginState<MenuItem>()
+ }
+
+ // Updates keyboardHoveredItemIndex if the up or down arrow key was
+ // pressed, or resets it if the keyboard cursor moved.
+ if (transaction.getMeta(pluginKey)?.selectedItemIndexChanged !== undefined) {
+ let newIndex = transaction.getMeta(pluginKey).selectedItemIndexChanged
+
+ // Allows selection to jump between first and last items.
+ if (newIndex < 0) {
+ newIndex = prev.items.length - 1
+ } else if (newIndex >= prev.items.length) {
+ newIndex = 0
+ }
+
+ next.keyboardHoveredItemIndex = newIndex
+ } else if (oldState.selection.from !== newState.selection.from) {
+ next.keyboardHoveredItemIndex = 0
+ }
+
+ return next
+ },
+ },
+
+ props: {
+ handleKeyDown(view, event) {
+ const menuIsActive = (this as Plugin).getState(view.state).active
+ const link = (this as Plugin).getState(view.state).link
+
+ // Doesn't handle other keystrokes if the menu isn't active.
+ if (!menuIsActive) {
+ return false
+ }
+
+ // Handles keystrokes for navigating the menu.
+ const { items, keyboardHoveredItemIndex } = pluginKey.getState(view.state)
+
+ // Moves the keyboard selection to the previous item.
+ if (event.key === 'ArrowUp') {
+ view.dispatch(
+ view.state.tr.setMeta(pluginKey, {
+ selectedItemIndexChanged: keyboardHoveredItemIndex - 1,
+ }),
+ )
+ }
+
+ // Moves the keyboard selection to the next item.
+ if (event.key === 'ArrowDown') {
+ view.dispatch(
+ view.state.tr.setMeta(pluginKey, {
+ selectedItemIndexChanged: keyboardHoveredItemIndex + 1,
+ }),
+ )
+ }
+
+ // Selects an item and closes the menu.
+ if (event.key === 'Enter') {
+ deactivate(view)
+ editor._tiptapEditor.chain().focus().run()
+ items[keyboardHoveredItemIndex].execute(editor, link)
Greptile
greptile
logic: Potential array index out of bounds error if items array is empty but keyboardHoveredItemIndex is set
suggested fix
+ if (items.length > 0 && keyboardHoveredItemIndex >= 0 && keyboardHoveredItemIndex < items.length) {
items[keyboardHoveredItemIndex].execute(editor, link)
}
diff block
let columns = file.split('/').collect::<Vec<&str>>();
if columns[0] == "files" {
Greptile
greptile
logic: Potential array index out of bounds if file path doesn't have enough segments
suggested fix
+ let columns = file.split('/').collect::<Vec<&str>>();
+ if columns.len() >= 3 && columns[0] == "files" {
diff block
+export function convertBytes(
+ bytes: number,
+ options: { decimals?: number; useBinaryUnits?: boolean } = {},
+): string {
+ const { useBinaryUnits = false, decimals = 2 } = options;
+
+ if (decimals < 0) {
+ throw new Error(`Invalid decimals ${decimals}`);
+ }
+
+ const base = useBinaryUnits ? 1024 : 1000;
+ const units = useBinaryUnits
+ ? ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
+ : ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+
+ const i = Math.floor(Math.log(bytes) / Math.log(base));
Greptile
greptile
logic: Math.log(0) will return -Infinity, causing array index out of bounds. Need to handle zero bytes case
suggested fix
+ if (bytes === 0) return '0 Bytes';
const i = Math.floor(Math.log(bytes) / Math.log(base));
diff block
let columns = file.split('/').collect::<Vec<&str>>();
if columns[0] == "files" {
metrics::STORAGE_WRITE_BYTES
- .with_label_values(&[columns[1], columns[2]])
+ .with_label_values(&[columns[1], columns[2], "remote"])
.inc_by(data_size as u64);
metrics::STORAGE_WRITE_REQUESTS
- .with_label_values(&[columns[1], columns[2]])
+ .with_label_values(&[columns[1], columns[2], "remote"])
.inc();
Greptile
greptile
logic: Potential array index out of bounds error - no bounds checking on columns[0], columns[1], columns[2] before accessing them. Should validate array length first.
suggested fix
+ let columns = file.split('/').collect::<Vec<&str>>();
+ if columns.len() >= 3 && columns[0] == "files" {
+ metrics::STORAGE_WRITE_BYTES
.with_label_values(&[columns[1], columns[2], "remote"])
+ .inc_by(data_size as u64);
+ metrics::STORAGE_WRITE_REQUESTS
.with_label_values(&[columns[1], columns[2], "remote"])
+ .inc();
diff block
],
metric: [
(s) => [s.metricIndex, s.experiment],
- (metricIndex: number, experiment: Experiment) => experiment.metrics[metricIndex],
+ (metricIndex: number | null, experiment: Experiment): ExperimentMetric | null => {
+ if (metricIndex === null) {
+ return null
+ }
+
+ // Check if the index is within the regular metrics array
+ if (metricIndex < experiment.metrics.length) {
+ return experiment.metrics[metricIndex] as ExperimentMetric
+ }
+
+ // If not, check shared metrics with primary type
+ const sharedMetricIndex = metricIndex - experiment.metrics.length
+ const sharedMetric = experiment.saved_metrics.filter((m) => m.metadata.type === 'primary')[
+ sharedMetricIndex
+ ]
Greptile
greptile
logic: Potential array index out of bounds error if no primary metrics exist in saved_metrics
suggested fix
+ const primaryMetrics = experiment.saved_metrics.filter((m) => m.metadata.type === 'primary');
+ const sharedMetric = sharedMetricIndex >= 0 && sharedMetricIndex < primaryMetrics.length
+ ? primaryMetrics[sharedMetricIndex]
+ : undefined;