crowdsecurity/crowdsec · error

unable to read schema file %s : %w

Error message

unable to read schema file %s : %w

What it means

After passing the path-safety check, loadAPISchema reads the schema file from <data_dir>/schemas/<filename> with os.ReadFile. Any read failure — missing file, permissions, is-a-directory — is wrapped as this error. The API request validator then cannot validate requests against the referenced schema.

Source

Thrown at pkg/appsec/appsec.go:1885

		if bouncerStatusCode == 0 {
			bouncerStatusCode = w.Config.BouncerBlockedHTTPCode
		}
	}

	return bouncerStatusCode, resp
}

const schemasSubDir = "schemas"

func (w *AppsecRuntimeConfig) loadAPISchema(ref, filename string, opts *apivalidation.SchemaOptions) error {
	if !filepath.IsLocal(filename) {
		return fmt.Errorf("schema filename %q must be relative to %s and stay within it", filename, schemasSubDir)
	}
	schemaPath := filepath.Join(w.DataDir, schemasSubDir, filename)
	w.Logger.Debugf("loading schema %s for ref %s", schemaPath, ref)
	schema, err := os.ReadFile(schemaPath)
	if err != nil {
		return fmt.Errorf("unable to read schema file %s : %w", schemaPath, err)
	}
	return w.RequestValidator.LoadSchema(ref, string(schema), opts)
}

func (w *AppsecRuntimeConfig) LoadAPISchemaWithName(ref string, filename string) error {
	return w.loadAPISchema(ref, filename, nil)
}

// LoadAPISchemaWithOptions behaves like LoadAPISchemaWithName but accepts a
// map of policy overrides. Supported keys:
//   - "on_route_not_found":             "drop" | "ignore"  (default: "drop")
//   - "on_method_not_allowed":          "drop" | "ignore"  (default: "drop")
//   - "on_unsupported_security_scheme": "drop" | "ignore"  (default: "drop")
func (w *AppsecRuntimeConfig) LoadAPISchemaWithOptions(ref string, filename string, opts map[string]any) error {
	schemaOpts, err := parseSchemaOptions(opts)
	if err != nil {
		return err
	}

View on GitHub (pinned to 909b515798)

Solutions

  1. Verify the file exists at <data_dir>/schemas/<filename> (ls the directory) and fix the configured name/typos
  2. Check file permissions so the crowdsec user can read it
  3. Confirm the data_dir config points to the directory that actually holds schemas/
  4. Copy or mount the schema file into the schemas directory if it's missing (e.g. in containers)
  5. Note the wrapped error: `no such file` vs `permission denied` vs `is a directory` point to different fixes

Example fix

// before
schema: userschema.json   # file not present
// after (after copying userschema.json into <data_dir>/schemas/)
schema: userschema.json
Defensive patterns

Strategy: validation

Validate before calling

p := filepath.Join(dataDir, "schemas", filename)
if fi, err := os.Stat(p); err != nil || fi.IsDir() {
    return fmt.Errorf("schema %s missing or invalid: %w", p, err)
}

Try / catch

if err := rt.LoadAPISchema(ref, filename); err != nil {
    if errors.Is(err, os.ErrNotExist) {
        log.Fatalf("schema file missing — install it into %s", schemasDir)
    }
    if errors.Is(err, os.ErrPermission) {
        log.Fatalf("fix permissions on schema file: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: os.ReadFile failing on the joined schemaPath: filename typo, schema never copied into the schemas dir, wrong data_dir setting, or read permissions.

Common situations: Referencing a schema file that was never installed; running crowdsec in a container where the schemas dir wasn't mounted; permission denied after hardening; filename case mismatch.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/e7b6004ba37cfe8a. Report an issue: GitHub.