passbolt/passbolt_api · error · CakeException

Could not read the file

Error message

Could not read the file: {0}

What it means

In KeyringInitCommand, after confirming the private key file exists, file_get_contents() returns false (PHP warning suppressed by @ or checked here) when the file cannot be read — typically due to filesystem permissions, unreadable ownership, or an I/O problem. The command throws because it cannot import a key it cannot read.

Solutions

  1. Fix ownership/permissions: chown www-data:www-data <keyfile> && chmod 600 <keyfile>, then re-run keyring init as the web server user.
  2. Run the command as the same user that owns the key: sudo su -s /bin/bash -c './bin/cake passbolt keyring init' www-data.
  3. Check the path is a regular file (ls -l) and the parent directories grant +x traversal.
  4. If SELinux denies access, restore correct context (restorecon) or check audit logs (ausearch -m avc).

Example fix

// before: root-owned key, command runs as www-data -> read fails
-rw------- root root /etc/passbolt/serverkey_private.asc
// after
$ chown www-data:www-data /etc/passbolt/serverkey_private.asc && chmod 600 /etc/passbolt/serverkey_private.asc
$ sudo su -s /bin/bash -c './bin/cake passbolt keyring init' www-data
Defensive patterns

Strategy: type-guard

Validate before calling

$path = Configure::read('passbolt.gpg.serverKey.private');
if (!is_file($path) || !is_readable($path)) {
    fwrite(STDERR, "Cannot read key file '{$path}' as user " . get_current_user() . ". Check owner/permissions.\n");
    exit(1);
}

Try / catch

try {
    $this->KeyringInitCommand->execute($args, $io);
} catch (CakeException $e) {
    if (str_starts_with($e->getMessage(), 'Could not read the file')) {
        // chown/chmod the key for the current user, or re-run as its owner
    }
}

Prevention

When it happens

Trigger: file_exists() passes but file_get_contents() fails on passbolt.gpg.serverKey.private: file owned by root with mode 600 while command runs as www-data, directory without execute permission, SELinux/AppArmor denial, or the path is a directory/special file.

Common situations: Key generated as root then command run as www-data (or vice versa); Docker image where key is mounted root-owned 600; SELinux enforcing on RHEL/Fedora; NFS/permissions issues after restore.

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 passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/f3c50086b97927ba. Report an issue: GitHub.

Appendix: source

Thrown at src/Command/KeyringInitCommand.php:68

    /**
     * @inheritDoc
     */
    public function execute(Arguments $args, ConsoleIo $io): ?int
    {
        parent::execute($args, $io);

        // Root user is not allowed to execute this command.
        $this->assertCurrentProcessUser($io, $this->processUserService);

        try {
            $filePath = Configure::read('passbolt.gpg.serverKey.private');
            if (!file_exists($filePath)) {
                throw new CakeException(__('The file does not exist: {0}', $filePath));
            }
            $armoredKey = file_get_contents($filePath);
            if ($armoredKey === false) {
                throw new CakeException(__('Could not read the file: {0}', $filePath));
            }
            // Import the private key in the OpenPGP keyring
            $gpg = OpenPGPBackendFactory::get();

            $io->out('Importing ' . $filePath);
            $gpg->importKeyIntoKeyring($armoredKey);
        } catch (CakeException $e) {
            $this->error($e->getMessage(), $io);
            $this->error('Could not import the server OpenPGP key into the keyring.', $io);

            return $this->errorCode();
        }

        $this->success('Keyring init OK', $io);

        return $this->successCode();
    }
}

View on GitHub (pinned to 31c1bbc10f)