MQL4 Array Out of Range: Causes, Fixes, and Code
By FXNook Lab team
An MQL4 “array out of range” error means the program tried to read or write an element outside the array’s valid indexes. Find the reported source line, compare the index with the array’s current size, then correct the loop or indexing assumption; increasing an array’s size without fixing that logic can leave the bug in place.
The most frequent causes are a loop that reaches one element too far, a history or buffer that has fewer elements than expected, and confusion about which bar is at index zero. The checks below apply to ordinary arrays, price series and indicator buffers in MQL4.
What the error means in MQL4
For a one-dimensional array containing n elements, valid indexes run from 0 to n-1. If an array has four elements, for example, its valid indexes are 0, 1, 2 and 3. Accessing index 4, or index -1, is outside the array.
This is a runtime error, not a compile error. MetaEditor can compile an expression such as values[i+1] because it cannot know every value that i will have while the program runs. The terminal detects the invalid access when that execution path is reached.
Start with the exact error in the terminal’s Experts or Journal log. MQL4 commonly reports the source file and line where the invalid access occurred. Open that file in MetaEditor and go to the reported line; if the line contains several array expressions, inspect every index on it, including indexes calculated in a function call.
The reported line is the place to investigate, but the cause can be an earlier calculation. For example, a bar shift may have been calculated from a count that is zero, or a loop variable may have reached a boundary on the previous iteration. Trace the index expression back to its inputs rather than changing the line blindly.
Check the array size and index
At the failing access, write down the array name, the index expression and the array size. ArraySize(array) returns the number of elements in an array. For a one-dimensional array, the access is valid only when the index is at least zero and less than that size.
Add a temporary log immediately before the access. It should print the value of the index and the array size under the same conditions that produce the error. Logging only the loop counter is not enough if the actual expression is i+1 or shift-1.
int index = i + 1;
int size = ArraySize(values);
Print("index=", index, " size=", size);
if(index < 0 || index >= size)
return;
Print("value=", values[index]);Use the check to diagnose and guard the access, not to hide a broken calculation. If the log reports an index of 10 and a size of 10, the upper bound is the problem: the last valid index is 9. If it reports size 0, find out why the array is empty before deciding whether the program should wait, return or handle the missing data another way.
For a multidimensional array, a valid index must be checked for each dimension. ArraySize gives the total number of elements, so use ArrayRange(array, dimension) when you need the length of a particular dimension. For example, with matrix[row][column], both row and column need to fit their respective ranges.
Common causes and fixes
| Cause | Why it fails | Correction |
|---|---|---|
| Off-by-one loop | A loop runs while i <= ArraySize(a), so its final iteration uses the size itself as an index. | For a loop that reads each element, use i < ArraySize(a). If the body reads a[i+1], stop before that next index would equal the size. |
| Empty or undersized array | The program assumes elements exist, but the array has size zero or fewer elements than the calculation requires. | Get the current size with ArraySize and handle sizes below the minimum required before accessing elements. |
| Assumed number of bars | Code requests a shift or neighbor bar that is not available in the chart or history loaded for the symbol and timeframe. | Use the available bar count, such as Bars in an EA or rates_total in OnCalculate, and verify there are enough bars for the operation. |
| Wrong series direction | The code assumes index zero is the oldest bar, or assumes it is the newest, when the array’s indexing direction is different. | Set or inspect the series direction where appropriate, then write the loop to match it. In a series array, index zero is the newest bar. |
A related trap is reusing a bar shift as if it were an ordinary array index. A shift of zero refers to the current bar in series-style price data, but an ordinary array’s index direction depends on how it is managed. Keep the meaning of each index clear: an array position is not automatically a bar shift unless the array is arranged as a series.
Rewrite an unsafe loop
This MQL4 script compiles, but its final iteration tries to read one element beyond the end of values. The array has four elements, so when i is 3, i+1 is 4, which is not a valid index.
#property strict
void OnStart()
{
double values[4] = {1.0, 1.1, 1.2, 1.3};
for(int i = 0; i < ArraySize(values); i++)
Print(values[i + 1]);
}The loop must stop while both the current element and its next neighbor exist. It also needs to handle arrays that have fewer than two elements. Here is the corrected script:
#property strict
void OnStart()
{
double values[4] = {1.0, 1.1, 1.2, 1.3};
int size = ArraySize(values);
if(size < 2)
{
Print("At least two elements are needed.");
return;
}
for(int i = 0; i < size - 1; i++)
{
Print("current=", values[i], " next=", values[i + 1]);
}
}The key is the loop condition: i < size - 1. On the last pass, i is size-2, so i+1 is size-1, the final valid index. This preserves the intended comparison between neighbors instead of skipping the boundary access or changing the data used by the calculation.
Handle price series and indicator buffers safely
MQL4 price series use shift indexing: the current bar is at index 0, the preceding bar is at index 1, and older bars have larger indexes. The series direction does not guarantee that all requested bars are available. A newly opened chart, a different timeframe or limited history can leave fewer bars than an EA or indicator expects.
In an EA, check that enough chart bars exist before requesting neighboring shifts. For example, code that needs the current and preceding bar must not assume the preceding bar is available when the chart has only one bar. For other symbols or timeframes, use the count and data for the requested series rather than assuming the chart’s Bars count describes that separate data.
In a custom indicator, rates_total is the number of bars available to the current OnCalculate call. A calculation that reads close[i+1] needs at least two bars and must not run at the oldest boundary with an invalid neighbor. Indicator buffers are sized by the terminal; do not try to resize a buffer bound with SetIndexBuffer. Set the buffer’s series direction explicitly if your calculations use shift-style indexing.
This complete MQL4 indicator calculates the difference between each bar’s close and the close of the preceding, older bar. It leaves the oldest bar empty because that bar has no older neighbor within the available data.
#property strict
#property indicator_separate_window
#property indicator_buffers 1
double DifferenceBuffer[];
int OnInit()
{
SetIndexBuffer(0, DifferenceBuffer, INDICATOR_DATA);
ArraySetAsSeries(DifferenceBuffer, true);
SetIndexStyle(0, DRAW_LINE);
SetIndexLabel(0, "Close difference");
return(INIT_SUCCEEDED);
}
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
ArraySetAsSeries(close, true);
ArraySetAsSeries(DifferenceBuffer, true);
if(rates_total < 2)
return(0);
DifferenceBuffer[rates_total - 1] = EMPTY_VALUE;
for(int i = rates_total - 2; i >= 0; i--)
DifferenceBuffer[i] = close[i] - close[i + 1];
return(rates_total);
}The loop begins at rates_total-2, so its largest neighbor index is rates_total-1, the last available bar. The guard handles insufficient history before any neighboring-bar access. The calculation deliberately recomputes the available bars on each call; for a more complex indicator, you can optimize recalculation using prev_calculated, but the starting and ending indexes still need to respect the same bounds.
After calling SetIndexBuffer, set the buffer direction you intend to use. Otherwise, assumptions about which bar a buffer index represents can differ from the series indexing used by the calculation. A loop can remain within the numerical bounds and still produce the wrong result if it walks through bars in the opposite direction from the one the logic expects.
Verify the fix in MetaEditor and Strategy Tester
- Compile the source. In MetaEditor, compile the edited
.mq4file and resolve any compile errors. A successful compile is a necessary first step, but it does not exercise every runtime index. - Reproduce the failing conditions. Use the same chart symbol, timeframe, indicator inputs or Strategy Tester settings that reached the reported line. If the error appeared on a live chart, attach the corrected program to a comparable chart and allow the same calculation path to run.
- Read the logs. Inspect the Experts and Journal tabs for a new array error and for your temporary index-and-size output. Remove or reduce diagnostic logging after you have confirmed the access stays inside the bounds.
- Test boundaries. Try limited history, a timeframe with fewer loaded bars, and cases where the array may be empty or contain only one element. For indicators, include a chart reload or a new-bar update so both initial calculation and subsequent calls are exercised.
- Check the result, not just the error. Verify that the loop still processes the intended bars and that the indicator’s buffer values correspond to the intended bar direction. A guard that skips every calculation can remove the message while also removing the behavior.
When using Strategy Tester, select the symbol, timeframe, date range and inputs that are relevant to the failure. A test with more history may not reveal a bug that occurs during startup with only a small number of bars, so include a short-history case where possible.
Fixes that hide the bug
Do not simply suppress the reported line or add an arbitrary offset to the index. That can prevent the invalid access while changing which bar or element the strategy reads. First identify what the index is meant to represent, then express the valid range in the loop or guard.
Likewise, resizing an array is not a general repair. A dynamic array may need to be resized when the program intentionally adds elements, but a larger allocation does not make an incorrect index meaningful. Indicator buffers managed by the terminal should not be resized manually after binding.
Keep boundary handling tied to the operation. A loop reading every element uses a different upper limit from a loop reading the next element too. A series calculation that reads an older bar needs a different starting boundary from one that reads a newer bar. Make those requirements visible in the loop condition, then verify the resulting values.
Practical next step
Start with the exact file and line in the Experts log, log the actual index and current array size, and reproduce the case with the smallest relevant history. If you want help with an MQL4 file, FXNook Lab’s MQL4/MQL5 error-fixing page explains how to upload code, have it compiled and see what changed. A compile check does not replace testing the runtime boundary conditions described above.
Questions
How do I find which array index is out of range in MQL4?
Find the source file and line reported in the Experts or Journal log, then inspect each array access on that line. Temporarily print the evaluated index and ArraySize(array) immediately before the access; the valid index must be at least zero and less than the size.
What is the valid index range for an MQL4 array?
For a one-dimensional array of size n, valid indexes are 0 through n-1. An empty array has no valid index, and multidimensional arrays require a valid index within each dimension.
How do I prevent array out of range errors in an MQL4 indicator?
Use rates_total to ensure enough bars exist before calculating, and set the intended indexing direction for price arrays and buffers. Bound every loop according to the neighbors it reads; a loop using close[i+1], for example, must stop before i+1 reaches rates_total.