nginx/nginx · critical

NGX_LOG_CRIT

NGX_LOG_CRIT

Error message

<ngx_read_file_n> read only %z of %z from \"%s\"

What it means

After the earlier stat/open accounted for buffer_size bytes, ngx_read_file returned fewer. This CRIT-level log signals the file shrank underneath the running request (rewritten or truncated in place) or a serious storage problem; the request fails with 500.

Source

Thrown at src/http/modules/ngx_http_mp4_module.c:1099

    if (mp4->buffer == NULL) {
        mp4->buffer = ngx_palloc(mp4->request->pool, mp4->buffer_size);
        if (mp4->buffer == NULL) {
            return NGX_ERROR;
        }

        mp4->buffer_start = mp4->buffer;
    }

    n = ngx_read_file(&mp4->file, mp4->buffer_start, mp4->buffer_size,
                      mp4->offset);

    if (n == NGX_ERROR) {
        return NGX_ERROR;
    }

    if ((size_t) n != mp4->buffer_size) {
        ngx_log_error(NGX_LOG_CRIT, mp4->file.log, 0,
                      ngx_read_file_n " read only %z of %z from \"%s\"",
                      n, mp4->buffer_size, mp4->file.name.data);
        return NGX_ERROR;
    }

    mp4->buffer_pos = mp4->buffer_start;
    mp4->buffer_end = mp4->buffer_start + mp4->buffer_size;

    return NGX_OK;
}


static ngx_int_t
ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
    u_char     *ftyp_atom;
    size_t      atom_size;
    ngx_buf_t  *atom;

View on GitHub (pinned to 3f6f7824d4)

Solutions

  1. Stop modifying served files in place: write a temp file and mv (rename) it into the final name.
  2. After an incident, stat the file and compare with the expected size; restore if damaged.
  3. Stage writes in a separate directory and swap via rename or symlink.
  4. If sizes are stable but short reads persist, investigate storage health.

Example fix

# before: in-place rewrite, readers can see a shrunken file mid-request
cp new_video.mp4 /data/videos/video.mp4

# after: atomic publish — rename() never exposes a partial file
cp new_video.mp4 /data/videos/.video.mp4.tmp && mv /data/videos/.video.mp4.tmp /data/videos/video.mp4
Defensive patterns

Strategy: retry

Validate before calling

# stability probe: a file changing size under requests is being rewritten in place
ts=$(stat -c %s /data/videos/v.mp4); sleep 5
[ "$ts" = "$(stat -c %s /data/videos/v.mp4)" ] && echo stable || echo 'file changing under readers!'

Try / catch

# client-side: retry once after a short delay; deploys finish and the read succeeds
async function fetchVideo(url) {
  for (let attempt = 0; attempt < 2; attempt++) {
    const res = await fetch(url);
    if (res.ok) return res;
    await new Promise(r => setTimeout(r, 500));
  }
  throw new Error('video unavailable');
}

Prevention

When it happens

Trigger: The file is replaced with a shorter one between the open in the content handler and this read: in-place rewrites by deploy scripts, logrotate-style truncation, or concurrent writers on shared storage.

Common situations: Deployment scripts overwriting live videos with cp; two writers publishing to the same path; failing disks returning short reads.

Related errors


AI-assisted analysis of nginx/nginx@3f6f7824d4 (2026-08-22). Data as JSON: /api/errors/b2ec18234cb979c4. Report an issue: GitHub.