// Legacy HostBackup API messages are an English wire contract shared with CLI callers. // Match complete, source-verified messages only; never derive a translation key from input. // This presentation-only adapter keeps unknown tool diagnostics and parameter text intact. type Translate = (key: string, params?: Record) => string const fixed = new Map([ ["path must be absolute", "backup.apiErrors.absolutePath"], ["this path is already configured", "backup.apiErrors.pathConfigured"], ["path is required", "backup.apiErrors.pathRequired"], ["path query parameter is required", "backup.apiErrors.pathQuery"], ["path not found", "backup.apiErrors.pathNotFound"], ["invalid job id", "backup.apiErrors.invalidJobId"], ["job not found", "backup.apiErrors.jobNotFound"], ["runner script not installed", "backup.apiErrors.runnerMissing"], ["no restore state to dismiss", "backup.apiErrors.noRestore"], ["invalid file name", "backup.apiErrors.invalidFile"], ["not found", "backup.apiErrors.notFound"], ["log path outside allowed directory", "backup.apiErrors.logOutside"], ["targets must be a list", "backup.apiErrors.targetsList"], ["empty expression", "backup.apiErrors.emptyExpression"], ["invalid id (use letters, digits, _ or -)", "backup.apiErrors.invalidId"], ["backend must be pbs, local or borg", "backup.apiErrors.backend"], ["backend must be pbs or borg", "backup.apiErrors.remoteBackend"], ["backend must be 'pbs' or 'borg'", "backup.apiErrors.remoteBackendQuoted"], ["profile_mode must be default or custom", "backup.apiErrors.profileMode"], ["borg cannot be attached (PVE vzdump only writes to pbs/local storages)", "backup.apiErrors.borgAttached"], ["borg is not a valid backend for attached jobs (PVE vzdump only writes to pbs/local storages)", "backup.apiErrors.borgAttachedLong"], ["pve_parent_job_id and pve_storage are required for attached", "backup.apiErrors.parentRequired"], ["paths must be a list of absolute paths", "backup.apiErrors.absolutePaths"], ["custom profile needs at least one path", "backup.apiErrors.customPath"], ["could not resolve default profile paths", "backup.apiErrors.defaultPaths"], ["retention must be an object", "backup.apiErrors.retentionObject"], ["pbs_repository is required", "backup.apiErrors.pbsRepository"], ["pbs_encrypt_mode must be 'none', 'new' or 'existing'", "backup.apiErrors.pbsEncryption"], ["failed to create PBS encryption keyfile", "backup.apiErrors.pbsKeyFailed"], ["failed to create PBS encryption keyfile (is proxmox-backup-client installed?)", "backup.apiErrors.pbsClientMissing"], ["borg_repo is required", "backup.apiErrors.borgRepository"], ["borg_encrypt_mode must be one of: none, repokey, keyfile, authenticated", "backup.apiErrors.borgEncryption"], ["encrypt_mode must be one of: none, repokey, keyfile, authenticated", "backup.apiErrors.encryption"], ["borg_passphrase is required when encryption is enabled (save it on the destination or pass one in the request)", "backup.apiErrors.borgPassphrase"], ["passphrase is required when encryption is enabled", "backup.apiErrors.passphrase"], ["password is required", "backup.apiErrors.password"], ["confirm_device must match device exactly (typo / safety check)", "backup.apiErrors.deviceConfirmation"], ["invalid device path", "backup.apiErrors.invalidDevice"], ["absolute mountpoint required", "backup.apiErrors.absoluteMountpoint"], ["archive layout not recognized — no rootfs/ found", "backup.apiErrors.archiveLayout"], ["archive not found", "backup.apiErrors.archiveMissing"], ["archive path is not a regular file", "backup.apiErrors.archiveNotFile"], ["invalid name", "backup.apiErrors.invalidName"], ["invalid staging_path", "backup.apiErrors.invalidStaging"], ["job name cannot be changed (delete + recreate to rename)", "backup.apiErrors.jobRename"], ["key_path must be absolute", "backup.apiErrors.absoluteKey"], ["mode must be \"local\" or \"ssh\"", "backup.apiErrors.mode"], ["name is required (letters, digits, _ or -)", "backup.apiErrors.nameRequired"], ["no log available for this job yet", "backup.apiErrors.noLog"], ["no manual PBS configs file", "backup.apiErrors.noPbsConfig"], ["output file missing", "backup.apiErrors.outputMissing"], ["parser output was not valid JSON", "backup.apiErrors.parserJson"], ["run_restore output was not valid JSON", "backup.apiErrors.restoreJson"], ["repo is required", "backup.apiErrors.repoRequired"], ["repo_name and snapshot are required", "backup.apiErrors.repoSnapshot"], ["repo_name and snapshot are required for borg", "backup.apiErrors.borgRepoSnapshot"], ["repo_name and snapshot are required for pbs", "backup.apiErrors.pbsRepoSnapshot"], ["repo_name is required", "backup.apiErrors.repoName"], ["restore tooling not installed on this host", "backup.apiErrors.restoreMissing"], ["server and datastore are required", "backup.apiErrors.serverDatastore"], ["snapshot is required", "backup.apiErrors.snapshotRequired"], ["source must be pbs, borg, or local", "backup.apiErrors.source"], ["ssh_port must be an integer between 1 and 65535", "backup.apiErrors.sshPort"], ["ssh_user, ssh_host and ssh_remote_path are required for ssh mode", "backup.apiErrors.sshRequired"], ["target has no repository path", "backup.apiErrors.repositoryPath"], ["task not found or expired", "backup.apiErrors.taskExpired"], ["invalid expression", "backup.apiErrors.invalidExpression"], ["no report emitted", "backup.apiErrors.noReport"], ["parse_manifest exited non-zero", "backup.apiErrors.parseManifestFailed"], ]) const parameterized: ReadonlyArray = [ [new RegExp("^job already exists: ([\\s\\S]+)$"), "backup.apiErrors.jobExists"], [new RegExp("^path does not exist on this host: ([\\s\\S]+)$"), "backup.apiErrors.pathMissing"], [new RegExp("^device does not exist: ([\\s\\S]+)$"), "backup.apiErrors.deviceMissing"], [new RegExp("^not a mountpoint: ([\\s\\S]+)$"), "backup.apiErrors.mountpoint"], [new RegExp("^local archive not found: ([\\s\\S]+)$"), "backup.apiErrors.localArchiveMissing"], [new RegExp("^Borg destination '([\\s\\S]+)' not found$"), "backup.apiErrors.borgDestination"], [new RegExp("^PBS destination '([\\s\\S]+)' not found$"), "backup.apiErrors.pbsDestination"], [new RegExp("^no password available for PBS repo '([\\s\\S]+)'$"), "backup.apiErrors.pbsPassword"], [new RegExp("^Borg repo \"([\\s\\S]+)\" not configured$"), "backup.apiErrors.borgRepoMissing"], [new RegExp("^PBS repo \"([\\s\\S]+)\" not configured$"), "backup.apiErrors.pbsRepoMissing"], [new RegExp("^Borg target not found: ([\\s\\S]+)$"), "backup.apiErrors.borgTargetMissing"], [new RegExp("^PBS repo not found: ([\\s\\S]+)$"), "backup.apiErrors.pbsTargetMissing"], [new RegExp("^PVE vzdump job not found: ([\\s\\S]+)$"), "backup.apiErrors.pveJobMissing"], [new RegExp("^cannot persist state: ([\\s\\S]+)$"), "backup.apiErrors.statePersist"], [new RegExp("^cannot prepare ssh dir: ([\\s\\S]+)$"), "backup.apiErrors.sshDirectory"], [new RegExp("^cannot read log: ([\\s\\S]+)$"), "backup.apiErrors.logRead"], [new RegExp("^could not update \\.env: ([\\s\\S]+)$"), "backup.apiErrors.envUpdate"], [new RegExp("^failed to persist PBS config: ([\\s\\S]+)$"), "backup.apiErrors.pbsConfigWrite"], [new RegExp("^failed to persist: ([\\s\\S]+)$"), "backup.apiErrors.persist"], [new RegExp("^failed to remove archive: ([\\s\\S]+)$"), "backup.apiErrors.archiveRemove"], [new RegExp("^failed to start: ([\\s\\S]+)$"), "backup.apiErrors.start"], [new RegExp("^failed to update borg-targets\\.txt: ([\\s\\S]+)$"), "backup.apiErrors.borgTargetsUpdate"], [new RegExp("^failed to update pbs-manual-configs\\.txt: ([\\s\\S]+)$"), "backup.apiErrors.pbsTargetsUpdate"], [new RegExp("^failed to update systemd units: ([\\s\\S]+)$"), "backup.apiErrors.unitsUpdate"], [new RegExp("^failed to write borg-targets\\.txt: ([\\s\\S]+)$"), "backup.apiErrors.borgTargetsWrite"], [new RegExp("^failed to write job files: ([\\s\\S]+)$"), "backup.apiErrors.jobWrite"], [new RegExp("^failed to write local-target\\.conf: ([\\s\\S]+)$"), "backup.apiErrors.localTargetWrite"], [new RegExp("^failed to write systemd units: ([\\s\\S]+)$"), "backup.apiErrors.unitsWrite"], [new RegExp("^format failed: ([\\s\\S]+)$"), "backup.apiErrors.format"], [new RegExp("^invalid on_calendar: ([\\s\\S]+)$"), "backup.apiErrors.calendar"], [new RegExp("^mount failed: ([\\s\\S]+)$"), "backup.apiErrors.mount"], [new RegExp("^parser invocation failed: ([\\s\\S]+)$"), "backup.apiErrors.parserInvocation"], [new RegExp("^preflight invocation failed: ([\\s\\S]+)$"), "backup.apiErrors.preflightInvocation"], [new RegExp("^public key not found after generation: ([\\s\\S]+)$"), "backup.apiErrors.publicKeyMissing"], [new RegExp("^retention\\.([\\s\\S]+) must be an integer$"), "backup.apiErrors.retentionInteger"], [new RegExp("^retention\\.([\\s\\S]+) must be non-negative$"), "backup.apiErrors.retentionNegative"], [new RegExp("^ssh-keygen failed: ([\\s\\S]+)$"), "backup.apiErrors.keygen"], [new RegExp("^systemctl failed: ([\\s\\S]+)$"), "backup.apiErrors.systemctl"], [new RegExp("^umount failed: ([\\s\\S]+)$"), "backup.apiErrors.unmount"], [new RegExp("^task is in state \"([\\s\\S]+)\", not completed$"), "backup.apiErrors.taskIncomplete"], [new RegExp("^unknown kind: ([\\s\\S]+)$"), "backup.apiErrors.unknownKind"], [new RegExp("^unknown mode \"([\\s\\S]+)\"$"), "backup.apiErrors.unknownMode"], [new RegExp("^([\\s\\S]+) is the built-in default and cannot be removed$"), "backup.apiErrors.defaultRemove"], [new RegExp("^([\\s\\S]+) is the built-in default; no need to add it$"), "backup.apiErrors.defaultAdd"], [new RegExp("^pbs_encrypt_mode='existing' but no keyfile is installed at ([\\s\\S]+) — import one first with POST /api/host-backups/pbs-encryption/import$"), "backup.apiErrors.keyImport"], [new RegExp("^PVE job storage mismatch \\(job uses ([\\s\\S]+), payload says ([\\s\\S]+)\\)$"), "backup.apiErrors.storageMismatch"], ] export function hostBackupErrorText(problem: unknown, t: Translate): string { const message = typeof problem === "string" ? problem : problem && typeof problem === "object" && "message" in problem && typeof problem.message === "string" ? problem.message : "" if (!message) return t("backup.apiErrors.requestFailed") const key = fixed.get(message) if (key) return t(key) // Keep unusually large tool output verbatim, without running pattern matching over it. if (message.length > 16384) return message for (const [pattern, patternKey] of parameterized) { const match = pattern.exec(message) if (match) return t(patternKey, { value: match[1], other: match[2] ?? "" }) } return message }