I’m currently trying to setup a simple Cloud Scheduler and Cloud Run Function combination, as outlines in https://cloud.google.com/scheduler/docs/tut-gcf-http.
Followed this guide, successfully deployed my Function (which works when tested locally using `functions-framework`) and setup the Cloud Scheduler Job. Settings look as follows:
resource "google_cloud_scheduler_job" "scheduled-export" {
name = "scheduled-export"
# Monthly, at the first of the month at 3am
schedule = "0 3 1 * *"
time_zone = "Europe/Berlin"
attempt_deadline = "60s"
retry_config {
retry_count = 1
}
http_target {
http_method = "POST"
uri = module.export-func.uri
oidc_token {
service_account_email = module.scheduler-account.email
}
body = base64encode(jsonencode({
output_bucket = module.export-bucket.name
}))
headers = {
"Content-Type" = "application/json"
}
}
}
`module.export-func.uri` is using the Cloud Fabric Module to create a Cloud Function and retrieves its non-deterministic URI. Bucket and Service Account are created in a similar fashion and deploy successfully.
Now, once I select Force Run on the Cloud Scheduler Task, it results in an HTTP/400 with INVALID_ARGUMENT being returned. Logs on Cloud Scheduler side look as follows:
{
"insertId": "REDACTED",
"jsonPayload": {
"@type": "type.googleapis.com/google.cloud.scheduler.logging.AttemptFinished",
"status": "INVALID_ARGUMENT",
"jobName": "projects/REDACTED/locations/REDACTED/jobs/scheduled-export",
"debugInfo": "URL_ERROR-ERROR_OTHER. Original HTTP response code number = 400",
"targetType": "HTTP",
"url": "https://REDACTED.a.run.app/"
},
"httpRequest": {
"status": 400
},
"resource": {
"type": "cloud_scheduler_job",
"labels": {
"project_id": "REDACTED",
"location": "REDACTED",
"job_id": "scheduled-export"
}
},
"timestamp": "2025-09-08T12:39:19.861522148Z",
"severity": "ERROR",
"logName": "projects/REDACTED/logs/cloudscheduler.googleapis.com%2Fexecutions",
"receiveTimestamp": "2025-09-08T12:39:19.861522148Z"
}
Here is the log entry of the Cloud Run Function Service:
{
"httpRequest": {
"cacheFillBytes": "0",
"cacheHit": false,
"cacheLookup": false,
"cacheValidatedWithOriginServer": false,
"latency": "0.012s",
"protocol": "HTTP/1.1",
"requestMethod": "POST",
"requestSize": "1296",
"requestUrl": "https://REDACTED.a.run.app/",
"responseSize": "413",
"status": 400,
"userAgent": "Google-Cloud-Scheduler"
},
"logName": "projects/REDACTED/logs/run.googleapis.com%2Frequests",
"payload": "payloadNotSet",
"receiveLocation": "REDACTED",
"receiveTimestamp": "2025-09-08T12:47:03.446740874Z",
"severity": "WARNING",
"timestamp": "2025-09-08T12:47:03.411460Z",
"traceSampled": true
}
Everything is set up in the same Region, same Project.
Upon manual inspection of the Cloud Scheduler Job, things are correctly entered. Body is set to a JSON string (not escaped, literally written as minified JSON), OIDC points to the correct account. Cloud Function URI is filled out.
My attention was caught by the element .payload being “payloadNotSet”, which indicates that the body is not being submitted correctly to the Cloud Function. But this is pure speculation. The error message is not very verbose, doesn’t tell me anything about what argument/parameter it is unhappy about and the logs seem to lack critical information.
My Function may also return HTTP/400, but would do so in a more verbose way, returning an error message including a payload, explaining what went wrong and how to fix it. This would appear in the Cloud Run logs, I suppose - but it doesn’t.
So this error, as far as I can tell, is produced by Google Cloud when invoking the Cloud Function.
However, my resource structure here is already so simple, that I cannot see any room for error really. Threads like INVALID_ARGUMENT occurs when trying to pass request body from Cloud Scheduler to Cloud Run Job use a slightly different approach and/or I wasn’t able to reproduce this solution for me.
I’m really frustrated after several hours of debugging such a basic functionality. I’m afraid to miss something blatently obvious, but I’m successfully deploying, permissions are not the culprit (according to the error message, which mentions arguments), so what could this be? Why is Cloud Functions rejecting my invocation?
Edit: After redeploying my Terraform code, the error became a HTTP/403 Unauthorized, despite the Service Account it is run as, being correct and not having changed. I double checked that this Account has the Invoker role.
Just to be sure, I re-ran Terraform, making sure that there is no state-drift. Running the Cloud Scheduler again changed the error into HTTP/500 URL unreachable. At this point, I’m not sure if Google is being serious here.
How can it be, that Cloud Scheduler, a glorified Crontab, is THAT difficult to configure while not providing any trace of what its errors mean? It literally takes me more time to debug this single scheduled invocation, that it took me to develop the entire Function. I might just consider dropping GCP and switching to GitHub Workflows to invoke it, since it works reliably and clearly states what is wrong.
