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
- Stop modifying served files in place: write a temp file and mv (rename) it into the final name.
- After an incident, stat the file and compare with the expected size; restore if damaged.
- Stage writes in a separate directory and swap via rename or symlink.
- 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
- Never overwrite served files in place; publish via write-temp-then-rename.
- Stage new versions in a new path or filename and switch the link atomically.
- Correlate CRIT short-read logs with deploy times to confirm the race source.
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.