From 41a90a10fbda4412001a508abea623a1d4fa3804 Mon Sep 17 00:00:00 2001 From: Nic O'Connor Date: Tue, 29 Sep 2026 16:49:10 +0000 Subject: [PATCH] zsh: verify opc/opd against the vault and explain failures op item edit can exit non-zero with "(404) Not Found" after an edit it did save (seen with op 2.34.1 and 2.39.0 on an existing item), which made opc report failure and skip the ops refresh. opc now reads the field back and opd re-checks the item's labels, then prints either the op:// reference it changed or what failed, op's message without its timestamp prefix, and where to look next. Co-Authored-By: Claude Opus 5.5 --- zsh/zshrc | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/zsh/zshrc b/zsh/zshrc index 8d625ca..9c44084 100644 --- a/zsh/zshrc +++ b/zsh/zshrc @@ -107,12 +107,35 @@ opc() { [[ -n $value ]] || { print -u2 "opc: empty value for $name; nothing changed"; return 1; } fi # [concealed] makes it a password field; an existing field of that name is updated. - _dotfiles_op sh -c 'op item edit "$OP_ITEM" --vault "$OP_VAULT" "$1[concealed]=$2" >/dev/null' \ - opc "$name" "$value" && ops + # op can exit non-zero with "(404) Not Found" after an edit it did save (seen with + # op 2.34.1 and 2.39.0), so its exit code is ignored: the field is read back instead. + _dotfiles_op sh -c ' + clean() { printf "%s" "${1:-(no output)}" | sed "s/^\[ERROR\] [0-9/]* [0-9:]* //"; } + ref="op://$OP_VAULT/$OP_ITEM/$1" + err=$(op item edit "$OP_ITEM" --vault "$OP_VAULT" "$1[concealed]=$2" 2>&1 >/dev/null) + if [ "$(op read "$ref" 2>/dev/null)" = "$2" ]; then + printf "opc: saved %s\n" "$ref" >&2 + else + printf "opc: could not save %s\n op said: %s\n check vault, item and service-account write access with: opl\n" \ + "$ref" "$(clean "$err")" >&2 + exit 1 + fi' opc "$name" "$value" && ops } opd() { _dotfiles_env_name "$1" opd || return - _dotfiles_op sh -c 'op item edit "$OP_ITEM" --vault "$OP_VAULT" "$1[delete]" >/dev/null' opd "$1" \ + # As in opc, op's exit code is unreliable here: check the item's field labels instead. + _dotfiles_op sh -c ' + clean() { printf "%s" "${1:-(no output)}" | sed "s/^\[ERROR\] [0-9/]* [0-9:]* //"; } + ref="op://$OP_VAULT/$OP_ITEM/$1" + err=$(op item edit "$OP_ITEM" --vault "$OP_VAULT" "$1[delete]" 2>&1 >/dev/null) + if ! item=$(op item get "$OP_ITEM" --vault "$OP_VAULT" --format json 2>&1); then + printf "opd: could not re-read %s to confirm the delete\n op said: %s\n" "$ref" "$(clean "$item")" >&2 + exit 1 + elif printf "%s" "$item" | jq -r ".fields[].label" | grep -qxF "$1"; then + printf "opd: could not delete %s\n op said: %s\n" "$ref" "$(clean "$err")" >&2 + exit 1 + fi + printf "opd: deleted %s\n" "$ref" >&2' opd "$1" \ && unset "$1" && ops } opl() {