Search documentation

    / documentation

    Troubleshooting

    Each symptom with the actual CLI message, the cause, and the fix — from command not found to a skill that does not appear in the tool.

    This page starts with the message shown in the terminal. Find the exact string for your case and follow the fix. All messages below are the actual strings from cli/src/cli.ts, cli/src/commands.ts, cli/src/install.ts and packages/core/src/maleta.ts; nothing here is paraphrased.

    #maleta: command not found

    Symptom. The shell responds with maleta: command not found (or equivalent) for any command.

    Cause. The maleta CLI is not installed globally, or npm's binary directory is not in PATH.

    Fix. Install the package from npm:

    Install and run
    bash
    npm install -g maleta.dev
    maleta --help

    Node >=20 is the requirement declared in engines, but it is only advisory: there is no runtime version check. If the command is still unavailable after installation, make sure npm's global binary directory is in PATH.

    #unknown command 'tray'

    Symptom. Running maleta tray prints [error] unknown command 'tray' and exits with code 2.

    Cause. The installed package predates tray support. Identify the global installation with npm ls -g maleta.dev --depth=0; an old release may not support maleta --version.

    Fix. Update the global package with npm install -g maleta.dev@latest, then run maleta tray again.

    #tray icon creation failed: <error>

    Symptom. On Linux, maleta tray reports a tray icon creation failure with guidance about D-Bus and StatusNotifierItem, then exits with code 4.

    Cause. The desktop session has no available StatusNotifierItem host, or the tray process cannot reach its session D-Bus. KDE panels normally provide a host; GNOME needs an AppIndicator extension.

    Fix. Enable the panel tray or AppIndicator extension and confirm that session D-Bus is available. Do not run the tray through SSH or in a headless session unless that session provides a StatusNotifierItem host.

    • A second maleta tray while one is already running: [error] tray is already running (pid <n>), exit 3.
    • The tray never signals READY: [error] tray did not become ready within 10 seconds, exit 4 (the child is killed).
    • It exits before READY: [error] tray exited before becoming ready (exit code <n>) or, when it exits 4 with no output, [error] tray is unavailable in this environment, exit 4.
    • The pid file cannot be created or the child pid cannot be resolved: exit 4.
    • It becomes ready and then dies within the 400 ms grace window: [error] tray exited immediately after becoming ready (exit code <n>), exit 1 — the success line is not printed.
    • Ctrl+C during startup kills the child and releases the pid file before the CLI exits.

    #unknown option '<x>'

    Symptom. stderr shows unknown option '<x>' followed by Run 'maleta --help' for usage., and the exit code is 2.

    Cause. The flag does not exist or is not valid in that position. Command options are --file, --tool, --dry-run, and --help (or -h). maleta --version is supported only as an isolated command. There is no -f, -t, -v, --force, or --quiet, and the file is not accepted as a positional argument. A stray word is reported as unexpected argument '<x>'; repeating a flag is duplicate --file or duplicate --tool; --tool with no value is --tool requires a value. A command name within edit distance 2 of a real one prints unknown command '<x>'. Did you mean '<y>'?, which wins over a trailing --help; maleta help prints the same help as --help.

    Fix. Use the supported flag instead: maleta validate --file ./maleta.json. Remember that --file is resolved against the cwd, so the relative path must be valid from the directory where the command runs.

    #--tool must be all, claude, codex, or agents

    Symptom. The message --tool must be all, claude, codex, or agents appears and the process exits with code 2.

    Cause. The value passed to --tool is not part of the enum, or the flag was used with init — in that case, the message is --tool is only supported by install and sync. A missing value is a different error: --tool requires a value. --tool applies to install and sync.

    Fix. Use one of the four values: all, claude, codex, or agents. If you want the target specified in the file, omit the flag: targetTool is used automatically.

    #refusing to overwrite existing file: <path>

    Symptom. maleta init prints [error] refusing to overwrite existing file: <caminho> and exits with code 3.

    Cause. The destination file already exists. init opens it with openSync(target, "wx"), which fails with EEXIST instead of overwriting it.

    Fix. Choose another path with --file (maleta init --file ./nova-maleta.json) or deliberately remove the existing file. [error] parent directory does not exist: <dir> means the parent is missing — create it first, because the CLI does not create the file path's directory tree; [error] permission denied while creating <path> means the parent is not writable; [error] could not create <path>: <msg> reports anything else. All three exit with code 1, and a failed write leaves no partial or empty file, so init can simply be retried.

    #not a regular file / file is UTF-16 / Maleta file is too large (--file)

    Symptom. Reading --file fails with [error] <path>: not a regular file, [error] <path>: file does not exist, [error] <path>: permission denied, [error] <path>: Maleta file is too large or [error] <path>: file is UTF-16; save it as UTF-8 and exits with code 1.

    Cause. The target is not a regular file — a directory, a FIFO, or a device such as /dev/stdin or /dev/zero — exceeds the 1 MiB limit enforced from the file size before anything is read, cannot be opened, or is UTF-16 (a BOM or a NUL-byte pattern). A UTF-8 BOM is accepted and stripped.

    Fix. Point --file at a real, readable UTF-8 file under 1 MiB, and save the document as UTF-8 if it came from an editor that picked UTF-16. A piped --file /dev/stdin is no longer accepted: write the bytes to a file first.

    #Invalid Maleta JSON

    Symptom. [error] <caminho>: Invalid Maleta JSON with exit code 1.

    Cause. The file contents are not valid JSON: a trailing comma, comment, single quotes, unquoted key, or a . instead of :. The entire JSON document is read before any schema validation.

    Fix. Compare it with the canonical example in Schema and run maleta validate --file ./maleta.json again. A document exported by the builder is already in the correct format: JSON.stringify(doc, null, 2) with a trailing newline.

    #Maleta must expose skills, plugins, targetTool, and targetOs at the top level

    Symptom. [error] <caminho>: Maleta must expose skills, plugins, targetTool, and targetOs at the top level.

    Cause. At least one of the following is missing from the root: slug, skills, plugins, targetTool, or targetOs — or the selection is inside a selection object. In the latter case, the message is Invalid Maleta format: selection fields must be at the top level.

    Fix. Move the selection fields to the top level and declare all required fields. The lists may be empty, but they must exist: "skills": [] and "plugins": [] are valid. If the error is Unsupported Maleta format, set schemaVersion to 1: there is no migration between versions.

    #targetOs '<x>' does not match the local host '<y>'; refusing <mode>

    Symptom. [error] targetOs 'windows' does not match the local host 'unix'; refusing install (or refusing sync), with exit code 4.

    Cause. The document declares an operating system that differs from the host. The manifest is portable, but execution is not: install and sync refuse to modify destinations that do not match the machine where they are running. validate does not perform this check.

    Fix. Set targetOs in the file to match the host, or run it on a machine that matches the declared value. Note that the same mismatch exits with 4 through the CLI and throws PlanError — which becomes exit 1 — when createInstallPlan is used as a library.

    #Skill destination collision '<name>' between '<id>' and '<id>'

    Symptom. [error] Skill destination collision 'reviewer' between 'github:acme%2Fskill-a:skill%2FSKILL.md@main' and 'github:acme%2Fskill-b:skill%2FSKILL.md@main', reported by both maleta validate and maleta install, before any network request.

    Cause. Two selected skills are competing for the same destination directory. The directory name is the skill's name, so a built-in skill and a GitHub skill with the same name collide even if they have different id values. The message names both source identities. A skill named shared has its own message: target directory name 'shared' is reserved for Maleta's shared builtin files; rename this skill. On Windows the comparison is case-insensitive, and a name ending with a dot or space, or matching a reserved device name (CON, NUL, COM1…LPT9), fails with Invalid skill name '<x>' for Windows.

    Fix. Rename one of the two entries in maleta.json so that the directory names are distinct. The name of a built-in source must still match the skill name; for the GitHub skill, name can be any value.

    #GitHub repository not found: <repo>

    Symptom. [error] GitHub repository not found: acme/skills; if this is a private repo, set GITHUB_TOKEN.

    Cause. The CLI made a diagnostic request to the specified repository and received 404. The repo does not exist, is private, or was entered incorrectly. The normalized value is always owner/name, without the scheme or .git.

    Fix. Verify the repository in repo — owner/name or the URL https://github.com/owner/name — and try again. A private repository is reachable only when GITHUB_TOKEN carries access to it, which is exactly what the message suggests.

    #GitHub ref not found: <ref>

    Symptom. [error] GitHub ref not found: v1.2.0.

    Cause. The repository exists, but the ref value does not match any branch, tag, or SHA. For 40-character hexadecimal refs, the CLI probes only the commits endpoint; for all others, it probes heads and tags.

    Fix. Change the ref to an existing branch or tag. If the goal was to track the default branch, omit the field—but read the warning about mutable branches in GitHub Skills.

    #GitHub skill path not found: <path>

    Symptom. [error] GitHub skill path not found: skills/reviewer.

    Cause. The repository and ref were found, but the specified path does not exist in that revision. This message is the most specific in the chain: the CLI only emits it after ruling out a missing repository and ref.

    Fix. Check the relative path within the repository. It is case-sensitive, uses / as the separator, and does not accept a leading slash.

    #skill path not found: <path> (uppercase SKILL.md is required at the skill root)

    Symptom. [error] skill path not found: skills/reviewer (uppercase SKILL.md is required at the skill root).

    Cause. The directory was resolved and the files were downloaded, but none of them is named exactly SKILL.md. The name is case-sensitive at that point: skill.md, Skill.md, and a marker in a nested directory no longer count. The file identifies the root of a skill; without it, the CLI does not materialize anything.

    Fix. Point path to the directory containing SKILL.md or specify the file itself—a path ending in SKILL.md is automatically reduced to its parent directory. Rename the marker to uppercase SKILL.md in the source repository.

    #GitHub 401, 403, and rate limit failures

    Symptom. One of [error] GitHub authentication failed; check GITHUB_TOKEN, [error] GitHub rate limit exceeded; retry after <reset>; set GITHUB_TOKEN to increase the limit, or [error] GitHub access denied; check repository access and GITHUB_TOKEN.

    Cause. 401 is a bad or missing token. 403 with x-ratelimit-remaining: 0 means the rate limit is exhausted, and the message carries the reset time reported by GitHub. Any other 403 means the token (or anonymous access) lacks repository access. The CLI reads the rate-limit headers to name the case; it never waits or retries automatically.

    Fix. Set GITHUB_TOKEN to a valid token and run the command again manually: GITHUB_TOKEN=ghp_exemplo maleta install --file ./maleta.json. The header is only sent to api.github.com, github.com and raw.githubusercontent.com; any other host is rejected with refusing GitHub token on a non-GitHub host. See Environment variables for details.

    #network timeout

    Symptom. [error] network timeout.

    Cause. A request exceeded the 10-second per-request limit and was aborted. There is no retry or backoff: resolution fails on the first slow attempt.

    Fix. Check connectivity to api.github.com, try again, and, if the repository is large, reduce the skill's file set. Because the entire plan is aborted before anything is written, a second run does not encounter partial state.

    #skill exceeded maximum size (5242880 bytes)

    Symptom. [error] skill exceeded maximum size (5242880 bytes) or, for an individual file, [error] skill file exceeded maximum size: <path> / [error] skill file exceeded maximum size (5242880 bytes).

    Also. A directory listing at the GitHub API cap of 1000 entries fails with [error] directory listing reached the GitHub API limit (1000 entries): <dir>; split the skill into a smaller directory.

    Cause. The total bytes in the resolved tree exceeded 5 MiB, or a single file exceeds the 5 MiB per-file limit. The total is accumulated as the tree is traversed.

    Fix. Reduce the contents of the directory specified by path in the source repository, or specify a smaller subdirectory. The limit is per skill, so splitting the selection into multiple entries helps when the source allows it.

    #skill exceeded maximum file count (200)

    Symptom. [error] skill exceeded maximum file count (200).

    Cause. The resolved tree contains more than 200 files. The count includes all regular files found recursively from the specified directory.

    Fix. Point path to a more specific directory or remove auxiliary skill files at the source. The two skill limits are independent — see the full table in Limits.

    Symptom. A destination directory that is itself a symbolic link refuses the run with an explanation naming MALETA_CLI_HOME, or a single skill is skipped with [skip] <label> -> <dest>: existing symbolic link at <path>; remove it and rerun while the other skills still install.

    Cause. Either a directory on the way to the destination is a symlink, or a skill's destination tree already contains a symbolic link where a directory is expected. The CLI refuses to write through a symlink because it can be repointed mid-run. The related check is destination is not a directory: <path>, when the segment exists but is not a directory.

    Fix. Replace the link with a real directory (move its target into place, then remove the link), remove the link inside the skill destination, or run with MALETA_CLI_HOME pointing to a real directory without symlinks: MALETA_CLI_HOME=/tmp/qa-home maleta install --file ./maleta.json. The CLI never writes through the link.

    #The skill did not appear in the tool

    Symptom. install or sync completes successfully, but the skill does not appear in the tool's list.

    Cause. There was probably a [skip] …. The possible cases are: [skip] builtin skill '<name>' is not present in claude/skills; not installed (the name does not exist in the packaged assets), [skip] plugin '<id>' is Claude-only and is unsupported for --tool <tool> and [skip] plugin '<id>' is not present in the local plugin manifest; not installed. Skipped items do not change the exit code: the run still exits with 0.

    Fix. Read the output line by line and confirm the correct destination: claude writes to ~/.claude/skills/<name>/, while codex and agents write to ~/.agents/skills/<name>/. Then restart the tool: the list of skills and plugins is loaded when the session starts. The complete path reference is available in Paths.

    #Computer already has a credential

    Symptom. [error] this computer already has a credential; run 'maleta logout' first (exit code 3).

    Cause. The computer already holds credentials in .maleta/credentials.json. Running maleta login again refuses to overwrite existing authorized credentials.

    Fix. If you want to re-authorize or switch accounts, run maleta logout first, then run maleta login again.

    #Computer is already bound to a Maleta

    Symptom. [error] a Maleta is already bound; run 'maleta detach' first (exit code 3).

    Cause. The computer is currently bound to another Cloud Maleta. V1 allows exactly one bound Maleta per computer.

    Fix. Unbind the current Maleta by running maleta detach, then run maleta attach <maleta> with the new Maleta.

    #Multiple Cloud Maletas share this name

    Symptom. [error] More than one Maleta in your account uses that name. Run 'maleta attach <id>' with the Maleta's id (exit code 1).

    Cause. You specified a slug or name in maleta attach that matches more than one Cloud Maleta in your account.

    Fix. Look up the specific Maleta's UUID on app.maleta.dev/maletas and attach using the ID: maleta attach <uuid>.

    #A maleta watch is already running

    Symptom. [error] a 'maleta watch' is already running on this computer (pid <pid>); stop it before removing (exit code 3).

    Cause. maleta prune --yes requires the exclusive watch lock (.maleta/watch.lock) to prevent deleting files while a reconciliation pass is active.

    Fix. Stop the background maleta watch process (or wait for it to finish), then run maleta prune --yes. You can preview candidate files at any time with maleta prune --dry-run without needing to stop watch.

    #Device state could not be read

    Symptom. maleta prune prints [error] corrupt state record; quarantined to <path> (would quarantine to <path> under --dry-run) and exits with code 1; maleta status prints [warn] this computer's record was unreadable and was quarantined to <path> on stderr.

    Cause. .maleta/state.json or .maleta/local-state.json is not valid JSON or has an unrecognized structure, so the CLI cannot verify ownership. A record that is simply missing is not this case: an empty home prints [ok] nothing recorded; nothing to remove and exits 0.

    Fix. The bad file is moved aside to .maleta/state.corrupt-<timestamp>.json (or the matching local-state name) and never deleted. Run maleta watch --once or maleta install again to rebuild a fresh record from the bound Cloud Maleta or the document.

    #Cloud Maleta limit reached (3 per account)

    Symptom. The web workspace warns with You reached the limit of 3 Maletas on the account (or API code limit_reached) when attempting to save a Maleta to the account.

    Cause. Each account can store up to 3 Cloud Maletas on the server.

    Fix. Open app.maleta.dev/maletas, click on an existing Cloud Maleta, and remove it from the account if no longer needed, or keep your additional Maletas stored locally in the browser (which holds up to 50 Maletas).

    #Cloud Maleta changed on another device

    Symptom. The web workspace alerts with This Maleta was changed on another device (or API code conflict) when trying to save changes.

    Cause. Optimistic concurrency control detected that another device updated the Cloud Maleta revision since you opened it.

    Fix. Use the actions presented on the row: choose "Reload from cloud" to adopt the remote changes, or "Save as copy" to preserve your local modifications as a new Maleta.

    Privacy preferences

    Choose what this origin may measure. You can change it here at any time.

    Essential storage

    This browser always keeps your theme, the Maletas you save here and this choice so the site works. They stay on this origin.

    Optional analytics

    Maleta.dev measures aggregate traffic and real-user performance only after you allow it.

    Current choice: not made yet, so measurement stays off.

    Privacy policy