Images upload (references) in random order in n8n — how to preserve sequence?

Hi everyone, I’m using n8n to upload multiple images (references) via an API request. Even though I provide all required fields and build what looks like a correct request, the images end up uploaded and used as references in a random order and the intended sequence breaks. Has anyone seen this behavior before? What could cause the server to reorder files (parallel requests, async processing, missing “position/index” field, etc.)? I’m looking for the best practice in n8n to ensure images are uploaded strictly in the correct order (1, 2, 3, …). Any tips or examples would be appreciated.

1 Like

Hey @Atol Welcome to the n8n community!

To keep image order consistent in n8n, you need to control how requests are sent and what data the API can sort on.

1. Send requests one-by-one

  • Use a Loop Over Items node with Batch Size = 1, wrapping your HTTP Request. This guarantees images are uploaded sequentially.

  • Alternatively, in the HTTP Request node, disable batching so each item processes in order.

2. Provide an explicit “position” field

  • Include $itemIndex + 1 in your request body as a position parameter, so the API can sort images reliably.

  • Or, add a Code node before uploading to assign a position property to each item.

If you can share your workflow or API you are using that might help us narrow down the issue. Hope it helps

I tried every method mentioned (sequential upload, batching, adding an index, etc.), but the system still assigns the images in a random sequence. In my case, the images are used as reference inputs for video generation. I first upload the images, and then I add them as reference website use image’s internal URL (the URL after uploaded into third system ). Even then, the references are attached in a random order.

1 Like

Hey @Atol, It seems the order issue is likely on the external video service, not n8n.

Key points:

  • n8n processes items sequentially within a run.
  • To ensure order, add an index (via Code node), then sort items before sending the final request.
  • Build your URL array in sorted order and send it as a single payload.

Next steps:

  • Attach a position to each image before upload.
  • After uploads, sort items by position.
  • Send the URLs in that order.

If the API reorders them despite this, the problem is on the service side, and you may need to pass explicit order parameters or check their docs.

Share sanitized request samples if you’d like me to review the ordering. If you can share your workflow we will be able to see how it handles the requests.

this is my saving of metadata

const b = $binary.data;

const idx = String($json.image_index).padStart(2, ‘0’);
const char = $json.character_id ?? ‘na’;
const original = $json.fileName ?? b.fileName ?? ‘file.png’;

b.fileName = ${idx}__${char}__${original};

return { json: $json, binary: { data: b } };

After all uploads im arranging everything collecting all data

const items = $input.all().map(i => i.json);

function uuidFromUrl(url) {
if (!url) return null;
const m = url.match(/_([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:.\w+)?$/i);
return m ? m[1] : null;
}

function indexFromFilename(filename) {
if (!filename) return null;
const m = filename.match(/^(\d{1,3})/); // "01…"
return m ? parseInt(m[1], 10) : null;
}

function pathFromUrl(url) {
try {
if (!url) return null;
return new URL(url).pathname.replace(/^/+/, ‘’);
} catch (e) {
return null;
}
}

const normalized = items.map((j) => {
const f0 = Array.isArray(j.files) ? j.files[0] : null;

const uploaded_filename =
j.uploaded_filename ??
f0?.filename ??
null;

const uploaded_url =
j.uploaded_url ??
f0?.url ??
null;

const uploaded_path =
j.uploaded_path ??
f0?.path ??
pathFromUrl(uploaded_url);

const uploaded_uuid =
j.uploaded_uuid ??
f0?.uuid ??
uuidFromUrl(uploaded_url);

const image_index =
j.image_index ??
indexFromFilename(uploaded_filename);

return {
image_index,
uploaded_uuid,
uploaded_url,
uploaded_path,
uploaded_filename,
// опционально: сохраняем оригинал, если надо дебажить
// _raw: j,
};
});

// Валидация
const missingIndex = normalized.filter(x => !(x.image_index > 0));
if (missingIndex.length) {
throw new Error(Missing image_index for ${missingIndex.length} item(s). Check filename prefix like "01__" or set image_index before upload.);
}

const missingUuid = normalized.filter(x => !x.uploaded_uuid);
if (missingUuid.length) {
const idxs = missingUuid.map(x => x.image_index).join(', ');
throw new Error(Missing uploaded_uuid for image_index: ${idxs}. Check upload response / url parsing.);
}

// Сортировка и сбор
normalized.sort((a, b) => a.image_index - b.image_index);

const ordered_uuids = normalized.map(x => x.uploaded_uuid);

// Удобные поля для прямой подстановки
const image_1_uuid = normalized.find(x => x.image_index === 1)?.uploaded_uuid ?? null;
const image_2_uuid = normalized.find(x => x.image_index === 2)?.uploaded_uuid ?? null;
const image_3_uuid = normalized.find(x => x.image_index === 3)?.uploaded_uuid ?? null;

return [{
json: {
ordered_uuids,
image_1_uuid,
image_2_uuid,
image_3_uuid,
ordered_files: normalized,
total: normalized.length,
}
}];

And after im sending api for post with ALL data

{{
(() => {
const files = Array.isArray($json.ordered_files) ? $json.ordered_files : ;

// 1) Берём только валидные записи с числовым image_index и строковым uploaded_url
const cleaned = files
  .map(f => ({
    image_index: Number(f?.image_index),
    uploaded_url: f?.uploaded_url,
  }))
  .filter(f =>
    Number.isFinite(f.image_index) &&
    f.image_index > 0 &&
    typeof f.uploaded_url === 'string' &&
    f.uploaded_url.length > 0
  );

// 2) Убираем дубли по image_index (оставляем первую встреченную), чтобы порядок был предсказуем
const seen = new Set();
const unique = [];
for (const f of cleaned) {
  if (seen.has(f.image_index)) continue;
  seen.add(f.image_index);
  unique.push(f);
}

// 3) Сортируем строго по image_index: 1,2,3...
unique.sort((a, b) => a.image_index - b.image_index);

const image_urls = unique.map(f => f.uploaded_url);

// 4) Минимальная проверка: нужна хотя бы 1 картинка (если хочешь разрешить 0 — убери этот блок)
if (image_urls.length === 0) {
  throw new Error('settings.image_urls is empty (no valid uploaded_url found)');
}

// 5) (опционально) ограничить максимум 3
// const image_urls_limited = image_urls.slice(0, 3);

return {
  seed: null,
  type: "references",
  upscale: false,
  mask_url: null,
  task_to_edit: null,
  aspect_ratio: "16:9",
  video_duration: 8,
  model_type: "veo3fast_r",
  image_urls
  // image_urls: image_urls_limited
};

})()
}}

Please help me to create post with all images in my positions

this is my workflow scheme, and code was in previous message, what im doing wrong ? Help please.