nginx/nginx · error

NGX_LOG_CRIT

NGX_LOG_CRIT

Error message

%s \"%s\" failed

What it means

Thrown by the stream access_log writer when a log file whose name contains runtime variables (dynamic log names) cannot be opened. ngx_stream_log_script_write() resolves the variable name per session and calls ngx_open_cached_file(); on a real errno it logs this CRIT message with the failing operation (of.failed, typically 'open()') and the resolved path. The log line is then silently dropped ('simulate successful logging'); proxying continues, only the log record is lost.

Solutions

  1. Create the missing directory and give the worker user write access: mkdir -p /logs/dir && chown nginx /logs/dir
  2. Fix permissions or SELinux context so the worker can create files there; verify with: sudo -u nginx touch /logs/dir/.probe
  3. Check that the variables in the filename resolve to sane values (no empty segments, no path-hostile characters, no over-long names)
  4. If EMFILE/ENFILE: raise worker_rlimit_nofile (or systemd LimitNOFILE) or enlarge open_log_file_cache inactive timeouts
  5. Watch error.log for this CRIT message after deploy; it only appears at runtime, not during nginx -t

Example fix

# before - $server_id resolves to a directory that was never created
access_log /var/log/nginx/$server_id/stream.log main;

# after - pre-create the tree for every possible value (provisioning step)
#   mkdir -p /var/log/nginx/s1 /var/log/nginx/s2 && chown nginx:nginx /var/log/nginx/s1 /var/log/nginx/s2
access_log /var/log/nginx/$server_id/stream.log main;
Defensive patterns

Strategy: validation

Validate before calling

# before reload: prove the worker user can create files under every dynamic log dir
sudo -u nginx bash -c 'mkdir -p /var/log/nginx/dyn && touch /var/log/nginx/dyn/.probe && rm /var/log/nginx/dyn/.probe'
# and confirm fd headroom for open_log_file_cache
ulimit -n; ls /proc/$(pgrep -o nginx)/fd | wc -l

Prevention

When it happens

Trigger: access_log with variables in the filename where the resolved directory does not exist (nginx never creates intermediate directories), the worker user lacks write permission on the directory, a symlink target is missing when open_log_file_cache is on, the resolved name exceeds NAME_MAX (ENAMETOOLONG), or open files are exhausted (EMFILE/ENFILE) with open_log_file_cache max=N.

Common situations: Per-client or per-listener log files like /logs/$server_id.log where the target directory was never created; containers or hardened hosts running workers as 'nobody' with restrictive permissions; SELinux/AppArmor denying writes to custom log paths; low ulimit -n combined with open_log_file_cache on busy stream endpoints.

Related errors


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

Appendix: source

Thrown at src/stream/ngx_stream_log_module.c:473

    lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_log_module);

    ngx_memzero(&of, sizeof(ngx_open_file_info_t));

    of.log = 1;
    of.valid = lscf->open_file_cache_valid;
    of.min_uses = lscf->open_file_cache_min_uses;
    of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;

    if (ngx_open_cached_file(lscf->open_file_cache, &log, &of,
                             s->connection->pool)
        != NGX_OK)
    {
        if (of.err == 0) {
            /* simulate successful logging */
            return len;
        }

        ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno,
                      "%s \"%s\" failed", of.failed, log.data);
        /* simulate successful logging */
        return len;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
                   "stream log #%d", of.fd);

    n = ngx_write_fd(of.fd, buf, len);

    return n;
}


#if (NGX_ZLIB)

static ssize_t
ngx_stream_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, ngx_int_t level,

View on GitHub (pinned to 3f6f7824d4)