From e05ed50916c9c2b6fdf44620835c826df9152556 Mon Sep 17 00:00:00 2001 From: Chris Arcand Date: Tue, 3 Feb 2026 18:01:43 -0600 Subject: [PATCH] Fix Vault.logical.read NoMethodError on HTTP 204 responses When Vault responds with HTTP 204 (No Content), the client.get() method returns nil. Previously, this nil was passed directly to Secret.decode(), which attempted to call .each on nil, resulting in a NoMethodError. This commonly occurred when reading from PKI engines without configured certificates, or any other endpoint that legitimately returns 204 No Content. Now Vault.logical.read() returns nil when the response is empty, consistent with its behavior for 404 responses. Fixes #241 --- CHANGELOG.md | 1 + lib/vault/api/logical.rb | 1 + spec/unit/logical_spec.rb | 50 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 spec/unit/logical_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 14f0e56..1cb6e81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ BUG FIXES - Fixed `encode_path` incorrectly encoding hyphens (`-`), which caused 403 errors on Vault 1.15+ [GH-350, GH-343] - Fixed `FrozenError` when loading the gem with OpenSSL 4.0.0+ by removing modification of `OpenSSL::SSL::SSLContext::DEFAULT_PARAMS`. Modern Ruby (3.1+) already has secure SSL defaults. [GH-366, GH-381] +- Fixed `Vault.logical.read` throwing `NoMethodError` when Vault responds with HTTP 204 (No Content). Now correctly returns `nil` for empty responses. [GH-241] ## v0.19.0 (December 3, 2025) diff --git a/lib/vault/api/logical.rb b/lib/vault/api/logical.rb index c1120de..9e9234d 100644 --- a/lib/vault/api/logical.rb +++ b/lib/vault/api/logical.rb @@ -48,6 +48,7 @@ def list(path, options = {}) def read(path, options = {}) headers = extract_headers!(options) json = client.get("/v1/#{encode_path(path)}", {}, headers) + return nil if json.nil? return Secret.decode(json) rescue HTTPError => e return nil if e.code == 404 diff --git a/spec/unit/logical_spec.rb b/spec/unit/logical_spec.rb new file mode 100644 index 0000000..e9e78d2 --- /dev/null +++ b/spec/unit/logical_spec.rb @@ -0,0 +1,50 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +require "spec_helper" + +module Vault + describe Logical do + let(:client) { double("client") } + subject { described_class.new(client) } + + describe "#read" do + it "returns nil when client.get returns nil (HTTP 204 No Content)" do + allow(client).to receive(:get).and_return(nil) + + result = subject.read("pki/ca") + + expect(result).to be_nil + end + + it "returns a Secret when client.get returns data" do + allow(client).to receive(:get).and_return({ + data: { foo: "bar" }, + lease_duration: 0, + renewable: false + }) + + result = subject.read("secret/test") + + expect(result).to be_a(Secret) + expect(result.data).to eq(foo: "bar") + end + + it "returns nil when client.get raises HTTPError with 404" do + allow(client).to receive(:get).and_raise(HTTPError.new("address", double(code: "404"))) + + result = subject.read("secret/missing") + + expect(result).to be_nil + end + + it "raises HTTPError for other error codes" do + allow(client).to receive(:get).and_raise(HTTPError.new("address", double(code: "500"))) + + expect { + subject.read("secret/error") + }.to raise_error(HTTPError) + end + end + end +end