redis/redis ยท error

%s

Error message

%s

What it means

The final fprintf in the loaderr block: prints the human-readable reason string 'err' (a newline-terminated '%s') that was set by whichever parsing/sanity path jumped to loaderr. This is the actionable message (e.g. 'unknown directive', 'wrong number of arguments', 'replicaof directive not allowed in cluster mode') and is followed immediately by exit(1). Without this line the banner and line number alone would not explain the failure.

Source

Thrown at src/config.c:651

    }

    /* To ensure backward compatibility and work while hz is out of range */
    if (server.config_hz < CONFIG_MIN_HZ) server.config_hz = CONFIG_MIN_HZ;
    if (server.config_hz > CONFIG_MAX_HZ) server.config_hz = CONFIG_MAX_HZ;

    sdsfreesplitres(lines,totlines);
    reading_config_file = 0;
    return;

loaderr:
    if (argv) sdsfreesplitres(argv,argc);
    fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR (Redis %s) ***\n",
        REDIS_VERSION);
    if (i < totlines) {
        fprintf(stderr, "Reading the configuration file, at line %d\n", linenum);
        fprintf(stderr, ">>> '%s'\n", lines[i]);
    }
    fprintf(stderr, "%s\n", err);
    exit(1);
}

/* Load the server configuration from the specified filename.
 * The function appends the additional configuration directives stored
 * in the 'options' string to the config file before loading.
 *
 * Both filename and options can be NULL, in such a case are considered
 * empty. This way loadServerConfig can be used to just load a file or
 * just load a string. */
#define CONFIG_READ_LEN 1024
void loadServerConfig(char *filename, char config_from_stdin, char *options) {
    sds config = sdsempty();
    char buf[CONFIG_READ_LEN+1];
    FILE *fp;
    glob_t globbuf;

    /* Load the file content */

View on GitHub (pinned to 3acc0c49cf)

Solutions

  1. Treat the printed err string as the primary diagnostic and resolve the condition it describes.
  2. Cross-reference the err text with the echoed '>>> ...' line above it to confirm cause and location agree.
  3. If the err mentions cluster mode, remove the conflicting directive (e.g. replicaof) when cluster-enabled yes is set.
  4. Search the Redis source/docs for the exact err string to find the directive's accepted forms.
Defensive patterns

Strategy: validation

Validate before calling

#!/usr/bin/env bash
# Error [22] ('%s') is the human-readable detail string the parser prints after the
# 'Reading the configuration file' banner. The actionable defense is to validate each
# directive's arity/value before the server reads it.
CONF="${1:-/etc/redis/redis.conf}"
# Minimal arity map for the most commonly mis-set directives.
declare -A ARITY=(
  [port]=1 [bind]=-1 [save]=-2 [appendonly]=1 [maxmemory]=1
  [maxmemory-policy]=1 [requirepass]=1 [dir]=1 [dbfilename]=1
)
while IFS= read -r raw; do
  line="${raw%%#*}"; line="$(echo "$line" | xargs)"
  [ -z "$line" ] && continue
  set -- $line; d="$1"; shift; argc=$#
  exp="${ARITY[$d]:-}"
  if [ -n "$exp" ] && [ "$exp" -ge 0 ] && [ "$argc" -ne "$exp" ]; then
    echo "directive '$d' expects $exp arg(s), got $argc" >&2; exit 1
  fi
done < "$CONF"

Type guard

// Narrow an unknown directive record to a known-good shape before applying it.
export type DirectiveName = 'port' | 'bind' | 'save' | 'appendonly' | 'maxmemory' | 'dir' | 'requirepass';
export interface ValidDirective { name: DirectiveName; args: readonly string[]; }
const KNOWN: Record<DirectiveName, number> = {
  port: 1, bind: -1, save: -2, appendonly: 1, maxmemory: 1, dir: 1, requirepass: 1,
};
export function isValidDirective(o: unknown): o is ValidDirective {
  if (typeof o !== 'object' || o === null) return false;
  const v = o as ValidDirective;
  if (!Array.isArray(v.args) || !v.args.every(a => typeof a === 'string')) return false;
  const want = KNOWN[v.name as DirectiveName];
  if (want === undefined) return false;
  return want < 0 ? v.args.length >= -want : v.args.length === want;
}

Try / catch

// Catch the parser's per-directive failure and present the detail string cleanly.
for (const [name, args] of parsedEntries) {
  try {
    applyDirective(name, args);
  } catch (e) {
    if (e instanceof DirectiveError) {
      // mirrors the server's '%s' detail line
      throw new Error(`config directive '${name}': ${e.message}`);
    }
    throw e;
  }
}

Prevention

When it happens

Trigger: Printed for every loaderr path: directive-not-found, wrong argument count, invalid enum/numeric value, and the cluster-mode sanity checks at config.c:624-626 and dbnum adjustment. The 'err' pointer is assigned the reason literal just before 'goto loaderr'.

Common situations: This is the line operators actually read to fix the problem. Common reasons: 'unknown directive name', 'wrong number of arguments', 'Invalid argument ... for directive ...', and the cluster-mode conflicts. The message text is the authoritative clue when the offending line itself looks syntactically fine.

Related errors


AI-assisted analysis of redis/redis@3acc0c49cf (2026-08-01). Data as JSON: /data/errors/ecbefb6a927721f3.json. Report an issue: GitHub.