Replacing vault secrets failed

Dear Tyk community,

I was facing a issue while loading secrets from the vault, this issue was on every gateway reload if there is any special character in vault data it was failing all dashboard apis to load, lets take it step by step:

On GW reload dashboard func called: FromDashboardService link

here FromDashboardService is calling dashboard API example below:

tyk-dash:3000/system/apis

The response we get in resp.Body:

list := model.NewMergedAPIList()
inBytes, err := io.ReadAll(resp.Body)

example response body:
{
    "Status": "OK",
    "Message": [{},{}],
    "Nonce": ""
}

In the Message we have api definitions list, now you will see a func replaceSecrets after reading resp.Body

inBytes = a.replaceSecrets(inBytes)

And now replaceSecrets function have the vault data which will replace keys in api definition list from calling vault API like example below:

request: http://127.0.0.1:8200/v1/secret/data/tyk-apis

response:
{
    "request_id": "767b1a9c-3082-f2b6-5387-43abcd0a8494",
    "lease_id": "",
    "renewable": false,
    "lease_duration": 0,
    "data": {
        "data": {
            "NEWENDPOINT": "\nget"
        },
        "metadata": {
            "created_time": "2025-07-28T07:59:53.959534133Z",
            "custom_metadata": null,
            "deletion_time": "",
            "destroyed": false,
            "version": 1
        }
    },
    "wrap_info": null,
    "warnings": null,
    "auth": null,
    "mount_type": "kv"
}

Now if there is any special character in vault like above NEWENDPOINT and after replacing this KV it will fail to load all API definitions and will give you an error Couldn't unmarshal api definition list

Due to this issue your other APIs which aren’t using any vault will be also failed due to unmarshal either it was already running in gateway or not.

I tried to resolve this issue by using tyk gateway oss source, and tested successfully below is the solution which worked for me:

replace

	inBytes = a.replaceSecrets(inBytes)

	err = json.Unmarshal(inBytes, &list)
	if err != nil {
		log.Error("Couldn't unmarshal api definition list")
		return nil, err
	}

With

	err = json.Unmarshal(data, &list)
		if err != nil {
			log.Error("Couldn't unmarshal api definition list")
			return nil, err
		}
	for i, api := range list.Message {
			apiBytes, err := json.Marshal(api)
			if err != nil {
				log.Errorf("Failed to marshal API index %d: %v", i, err)
				continue
			}
			// Replace secrets in JSON
			newBytes := a.replaceSecrets(apiBytes)
			// Unmarshal back to MergedAPI
			var updatedAPI model.MergedAPI
			err = json.Unmarshal(newBytes, &updatedAPI)
			if err != nil {
				log.Errorf("Failed to unmarshal updated API index %d: %v", i, err)
				continue
			}
			// Replace original
			list.Message[i] = updatedAPI
		}

Kindly do let me know if this is a clean fix for you or if you have any other solution.

Thanks,
Shebby

1 Like