usememos/memos · error

file must resolve to a regular file

Error message

file must resolve to a regular file

What it means

readDeploymentProtoJSON stats the bootstrap file path before opening; if it resolves to anything other than a regular file (directory, device, socket, symlink-to-dir) it returns "file must resolve to a regular file". This guards deployment-config loading (e.g. memos-idp-*.json identity provider files) from reading odd filesystem objects.

Solutions

  1. Point the config path at an actual JSON file, not its parent directory
  2. In Docker, mount the single file (or a directory and reference the file inside it)
  3. Run stat <path> to confirm regular-file mode before startup

Example fix

# before
-v ./idp:/var/opt/memos/idp.json # mounts a directory as the "file"
# after
-v ./idp/memos-idp-oauth.json:/var/opt/memos/idp/memos-idp-oauth.json
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Stat(path)
if err != nil {
    return err
}
if !info.Mode().IsRegular() {
    return fmt.Errorf("config path %s is not a regular file", path)
}

Prevention

When it happens

Trigger: Passing a directory or /dev/stdin-style path as the deployment config file; a symlink chain ending in a directory; a fifo created by tooling where the JSON file was expected.

Common situations: Container mounts pointing at a directory instead of a file; config generators that create a directory named like the file; copy-paste errors in the --data bootstrap path.

Related errors


AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15). Data as JSON: /api/errors/c0c7804540861475. Report an issue: GitHub.

Appendix: source

Thrown at store/deployment_config.go:128

		identityProviders: map[string]*storepb.IdentityProvider{},
		instanceSettings:  map[storepb.InstanceSettingKey]*storepb.InstanceSetting{},
	}
}

func isIdentityProviderDeploymentFilename(name string) bool {
	// The original database-writing bootstrap accepted every filename with this
	// prefix and suffix. Continue loading those names so an upgrade cannot
	// silently fall back to stale credentials stored in the database.
	return strings.HasPrefix(name, "memos-idp-") && strings.HasSuffix(name, ".json")
}

func readDeploymentProtoJSON(path string, message proto.Message) error {
	info, err := os.Stat(path)
	if err != nil {
		return errors.Wrap(err, "failed to inspect file")
	}
	if !info.Mode().IsRegular() {
		return errors.New("file must resolve to a regular file")
	}
	file, err := os.Open(path)
	if err != nil {
		return errors.Wrap(err, "failed to open file")
	}
	defer file.Close()
	info, err = file.Stat()
	if err != nil {
		return errors.Wrap(err, "failed to inspect file")
	}
	if !info.Mode().IsRegular() {
		return errors.New("file must resolve to a regular file")
	}
	content, err := io.ReadAll(io.LimitReader(file, maxDeploymentConfigurationSize+1))
	if err != nil {
		return errors.Wrap(err, "failed to read file")
	}
	if len(content) > maxDeploymentConfigurationSize {

View on GitHub (pinned to 14d757ce1f)