getSignedUrl giving "SigningError: Failure from metadata server"

I'm using Google Cloud Functions with an HTTP trigger to create an endpoint for an API call. This endpoint is using the getSignedUrl function from the Cloud Storage package. I wanted to return a signed URL so the client can PUT to that URL to upload a file.

I'm getting inconsistent behavior with this API. Sometimes I will get back a URL from the function. Then I tried to update the bucket name, and I get this error:

{ SigningError: Failure from metadata server.
at /user_code/node_modules/@google-cloud/storage/src/file.js:1715:16
at getCredentials (/user_code/node_modules/@google-cloud/storage/node_modules/google-auto-auth/index.js:264:9)
at googleAuthClient.getCredentials (/user_code/node_modules/@google-cloud/storage/node_modules/google-auto-auth/index.js:148:11)
at process._tickDomainCallback (internal/process/next_tick.js:135:7) message: 'Failure from metadata server.' }

If I change the bucket name back, I still see this error. I've tried different things such as deploying from the terminal. That gave me the same error. I also created the function with the browser, and that started to work. After I tried updating the bucket name, it stopped working.

From googling around, I'm thinking this is a permission or ACL issue, but I'm not sure how to confirm it. The documentation says that functions run using <YOUR_PROJECT_ID>@appspot.gserviceaccount.com service account, which I can confirm in the general tab of the function. I can see in IAM that this service account has the Service Account Token Creator, which says:

Impersonate service accounts (create OAuth2 access tokens, sign blobs or JWTs, etc).

The documentation for getSignedUrl says:

In those environments, we call the signBlob API to create a signed URL. That API requires either the https://www.googleapis.com/auth/iam or https://www.googleapis.com/auth/cloud-platform scope, so be sure they are enabled.

I feel like this should be enough for it to work. I don't know how to explicitly check for scopes.

The only other thing I've tried is to run sign-blob from the terminal. I ran this:

gcloud iam service-accounts sign-blob --iam-account=<my-project-id>@appspot.gserviceaccount.com input.file output.file

and I get back this error:

ERROR: (gcloud.iam.service-accounts.sign-blob) PERMISSION_DENIED: Permission iam.serviceAccounts.signBlob is required to perform this operation on service account projects/-/serviceAccounts/[email protected].

I don't know how to check that my service account has this permission. I've tried looking for it in IAM on the website, the gcloud terminal program, and the permissions set on my buckets.

Here is the code for my function:

const storage = require('@google-cloud/storage')();

exports.getSignedUrl = (req, res) => {

    if(req.method === 'POST') {

        // Perform any authorization checks here to assert
        // that the end user is authorized to upload.

        const myBucket = storage.bucket('my-bucket-name');
        const myFile = myBucket.file(req.body.filename);
        const contentType = req.body.contentType;

        // This link should only last 5 minutes
        const expiresAtMs = Date.now() + 300000;
        const config = {
            action: 'write',
            expires: expiresAtMs,
            contentType: contentType
        };

        myFile.getSignedUrl(config, function(err, url) {
            if (err) {
                console.error(err);
                res.status(500).end();
                return;
            }
            res.send(url);
        });
    } else {
        res.status(405).end();
    }
}

Has anyone seen this before, or know why this is happening? Or do you know how I can check if my service account has these permissions or scopes? I feel like this may be a bug in GCP, but it's likely that I'm missing something. I've been stuck on this for a while, and I'd appreciate any help you may have!


I solved it by adding "Cloud Functions Service Agent" role to my "App Engine default service account" service account.

Go to: https://console.cloud.google.com/iam-admin/iam

find "Cloud Functions Service Agent"
manage roles

I got guided with screen clips in @rscotten comment: https://github.com/googleapis/nodejs-storage/issues/150


After some trial and error, I found out that @google-cloud/storage version 1.6.0 was giving me this behavior. I downgraded to 1.5.2 and it started to work.

I opened up a GitHub issue for this: https://github.com/googleapis/nodejs-storage/issues/150