netbirdio/netbird · error

failed to presign URL

Error message

failed to presign URL

What it means

HTTP 500 from the S3-backed handler when presignClient.PresignPutObject fails. Presigning is local SigV4 signing, but the AWS SDK for Go v2 still needs resolvable credentials and a region (AWS_REGION is required at startup, BUCKET selects the S3 backend); the SDK error is logged server-side as 'Presign error'.

Source

Thrown at upload-server/server/s3.go:63

}

func (s *sThree) handlerGetUploadURL(w http.ResponseWriter, r *http.Request) {
	if !isValidRequest(w, r) {
		return
	}

	objectKey := getObjectKey(w, r)
	if objectKey == "" {
		return
	}

	req, err := s.presignClient.PresignPutObject(s.ctx, &s3.PutObjectInput{
		Bucket: aws.String(s.bucket),
		Key:    aws.String(objectKey),
	}, s3.WithPresignExpires(15*time.Minute))

	if err != nil {
		http.Error(w, "failed to presign URL", http.StatusInternalServerError)
		log.Errorf("Presign error: %v", err)
		return
	}

	respondGetRequest(w, req.URL, objectKey)
}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Give the server resolvable credentials: export AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY (plus AWS_SESSION_TOKEN for STS) or attach an IAM role
  2. Verify with 'aws sts get-caller-identity' run in the same environment as the server
  3. Confirm BUCKET and AWS_REGION are set for the process (missing AWS_REGION aborts startup earlier with a Fatal log)
  4. For IMDS timeouts, set AWS_EC2_METADATA_DISABLED=true together with explicit keys

Example fix

# before: no credentials reachable -> every GET /upload-url returns 500
docker run -e BUCKET=mybucket -e AWS_REGION=eu-west-1 nb/upload-server

# after: credentials passed through
export AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=...
docker run -e BUCKET=mybucket -e AWS_REGION=eu-west-1 \
  -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY nb/upload-server
Defensive patterns

Strategy: validation

Validate before calling

// smoke test at server startup: fail fast if presigning cannot work
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(region))
if err != nil {
	log.Fatalf("aws config: %v", err)
}
pc := s3.NewPresignClient(s3.NewFromConfig(cfg))
if _, err := pc.PresignPutObject(ctx, &s3.PutObjectInput{
	Bucket: aws.String(bucket), Key: aws.String("healthcheck"),
}, s3.WithPresignExpires(time.Minute)); err != nil {
	log.Fatalf("presign unavailable: %v", err)
}

Try / catch

A 500 'failed to presign URL' usually repeats until credentials are fixed: check the server log ('Presign error'), run aws sts get-caller-identity in the server environment, then retry the GET /upload-url.

Prevention

When it happens

Trigger: No credentials reachable by config.LoadDefaultConfig (no AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY, no profile, no IAM role); EC2/ECS/K8s pod where IMDS or IRSA is unreachable so credential resolution fails at presign time; expired STS session tokens; malformed shared config file.

Common situations: Local development without 'aws configure'; Kubernetes pod missing the service-account role annotation or env; AssumeRole sessions expiring under a long-running server; credentials working yesterday but the token rotated.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/2174fc3d9adedfcb. Report an issue: GitHub.