Repository: DataDog/dd-trace-py

Issue:
ddtrace.profiling.exporter.http.UploadFailed: 400 Bad request

### Which version of dd-trace-py are you using?
```
ddtrace[profiling]==0.37.1
```

### Which version of the libraries are you using?

```
aiodns==2.0.0
aiohttp==3.5.4
apipkg==1.5
appnope==0.1.0
argcomplete==1.9.3
asn1crypto==0.24.0
async-timeout==3.0.1
atomicwrites==1.2.1
attrs==19.1.0
Authlib==0.10
Babel==2.8.0
base58==1.0.0
beautifulsoup4==4.6.3
blinker==1.4
boto3==1.10.44
botocore==1.13.44
CacheControl==0.12.6
cachetools==3.1.0
certifi==2019.6.16
cffi==1.12.3
cfn-flip==1.2.1
chardet==3.0.4
Click==7.0
cms-sdk==0.0.8
cookies==2.2.1
coreapi==2.3.3
coreschema==0.0.4
coverage==4.5.2
cryptography==2.4.2
ddtrace==0.37.1
decorator==4.2.1
defusedxml==0.5.0
Django==2.1.10
django-allauth==0.41.0
django-choices==1.7.0
django-cors-headers==3.0.2
django-extensions==2.2.1
django-filter==2.2.0
django-health-check==3.12.1
django-model-utils==3.2.0
django-mysql==3.2.0
django-mysql-geventpool==0.2.5
django-phonenumber-field==4.0.0
django-redis==4.10.0
django-replicated==2.6.1
django-rest-auth==0.9.5
django-safedelete==0.5.1
django-storages==1.7.1
django-summernote==0.8.11.4
django-uuidfield==0.5.0
djangorestframework==3.9.1
djangorestframework-jwt==1.11.0
dnspython==1.16.0
docutils==0.14
drf-yasg==1.16.1
durationpy==0.5
elastic-apm==5.2.2
elastic-app-search==7.5.0
execnet==1.5.0
factory-boy==2.12.0
Faker==1.0.1
fcm-django==0.2.19
firebase-admin==3.2.1
freezegun==0.3.10
future==0.16.0
gevent==1.4.0
google==2.0.1
google-api-core==1.16.0
google-api-python-client==1.7.10
google-auth==1.11.0
google-auth-httplib2==0.0.3
google-cloud==0.34.0
google-cloud-bigquery==1.17.0
google-cloud-core==1.2.0
google-cloud-firestore==1.6.1
google-cloud-storage==1.25.0
google-resumable-media==0.5.0
googleapis-common-protos==1.6.0
greenlet==0.4.15
grpcio==1.18.0
gunicorn==20.0.4
hjson==3.0.1
httplib2==0.12.1
iamport-rest-client==0.7.0
idna==2.8
idna-ssl==1.1.0
inflection==0.3.1
intervaltree==3.0.2
ipdb==0.10.3
ipython==6.2.1
ipython-genutils==0.2.0
itunes-iap==2.6.0
itypes==1.1.0
jedi==0.11.1
Jinja2==2.10
jmespath==0.9.3
kappa==0.6.0
MarkupSafe==1.1.1
mongoengine==0.17.0
more-itertools==5.0.0
msgpack==0.6.2
multidict==4.5.2
mysqlclient==1.3.14
newrelic==5.0.2.126
nplusone==0.9.0
oauthlib==2.0.6
packaging==19.1
parso==0.1.1
pexpect==4.3.1
phonenumberslite==8.12.1
pickleshare==0.7.4
pip-tools==4.5.1
pipdeptree==0.13.2
placebo==0.9.0
pluggy==0.8.1
prettyexc==0.6.0
prompt-toolkit==1.0.15
protobuf==3.6.1
psutil==5.6.3
ptyprocess==0.5.2
py==1.7.0
pyasn1==0.4.5
pyasn1-modules==0.2.4
pycares==3.0.0
pycparser==2.19
pycrypto==2.6.1
pyfcm==1.4.5
Pygments==2.2.0
pyjsbn-rsa==0.3
PyJWT==1.6.0
pymongo==3.8.0
pynamodb==4.1.0
pyOpenSSL==19.0.0
pyparsing==2.4.2
pytest==4.1.1
pytest-cov==2.6.1
pytest-django==3.4.5
pytest-factoryboy==2.0.3
pytest-forked==1.0.1
pytest-mock==1.10.2
pytest-xdist==1.26.0
python-dateutil==2.6.1
python-slugify==1.2.4
python3-openid==3.1.0
pytz==2018.9
PyYAML==5.1.1
raven==6.10.0
redis==3.0.1
redis-py-cluster==2.0.0
requests==2.22.0
requests-mock==1.5.2
requests-oauthlib==0.8.0
requests-toolbelt==0.8.0
responses==0.10.5
rsa==4.0
ruamel.yaml==0.15.86
s3transfer==0.2.1
sentry-sdk==0.14.1
simplegeneric==0.8.1
simplejson==3.13.2
six==1.12.0
slackclient==2.0.1
sortedcontainers==2.1.0
sqlparse==0.2.3
tenacity==6.1.0
text-unidecode==1.2
toml==0.10.0
tqdm==4.32.2
traitlets==4.3.2
troposphere==2.4.9
typing==3.7.4.1
typing-extensions==3.7.4.1
Unidecode==1.1.1
uritemplate==3.0.0
urllib3==1.25.3
wcwidth==0.1.7
websocket-client==0.48.0
Werkzeug==0.15.5
wsgi-request-logger==0.4.6
XlsxWriter==1.1.1
yarl==1.3.0
```
### How can we reproduce your problem?
```
In [1]: from ddtrace.profiling import Profiler

In [2]: prof = Profiler()

In [3]: prof.start()

In [4]: timezone.now()
Out[4]: datetime.datetime(2020, 5, 22, 8, 43, 50, 553507, tzinfo=<UTC>)

In [5]: prof.stop()
ERROR Unable to export 3082 events: ddtrace.profiling.exporter.http.UploadFailed: Unable to upload: urllib.error.HTTPError: HTTP Error 400: Bad request
```

### What is the result that you get?
```
ERROR Unable to export 3082 events: ddtrace.profiling.exporter.http.UploadFailed: Unable to upload: urllib.error.HTTPError: HTTP Error 400: Bad request
```



Discussion:
Hi,

Could you try with the 0.38.0 version?
Do you have any `DD_` variable set in your env (except the API key)?
still not working with ddtrace=0.38.0

**Test**
```
In [1]: from ddtrace.profiling import Profiler

In [2]: prof = Profiler()

In [3]: prof.start()

In [4]: timezone.now()
Out[4]: datetime.datetime(2020, 5, 23, 7, 35, 52, 389564, tzinfo=<UTC>)

In [5]: prof.stop()

ERROR Unable to export 14410 events: ddtrace.profiling.exporter.http.UploadFailed: Unable to upload: urllib.error.HTTPError: HTTP Error 400: Bad request

ERROR Unable to export 1347 events: ddtrace.profiling.exporter.http.UploadFailed: Unable to upload: urllib.error.HTTPError: HTTP Error 400: Bad request
```

**Env**
```
- name: DD_ENV
  value: staging
- name: DD_API_KEY
  value: XXXX
- name: DD_PROFILING_TAGS
  value: "env:staging,service:test"
- name: DD_SERVICE_NAME
  value: test
- name: DD_DJANGO_ANALYTICS_ENABLED
  value: "True"
- name: DD_DJANGO_DATABASE_SERVICE_NAME_PREFIX
  value:test-db-
- name: DD_DJANGO_CACHE_SERVICE_NAME
  value: test-cache
- name: DD_AGENT_HOST
  valueFrom:
  fieldRef:
      fieldPath: status.hostIP
```
Could you double-check your API key? This is usually the source of 400 errors.
@jd  Ohh.. Just my mistake. thanks 😄 

Resolving patch:
diff --git a/ddtrace/profiling/exporter/http.py b/ddtrace/profiling/exporter/http.py
--- a/ddtrace/profiling/exporter/http.py
+++ b/ddtrace/profiling/exporter/http.py
@@ -173,7 +173,9 @@ def export(self, events, start_time_ns, end_time_ns):
         except tenacity.RetryError as e:
             raise UploadFailed(e.last_attempt.exception())
         except error.HTTPError as e:
-            if e.code == 404 and not self.api_key:
+            if e.code == 400:
+                msg = "Server returned 400, check your API key"
+            elif e.code == 404 and not self.api_key:
                 msg = (
                     "Datadog Agent is not accepting profiles. "
                     "Agent-based profiling deployments require Datadog Agent >= 7.20"


Test patch:
diff --git a/tests/profiling/exporter/test_http.py b/tests/profiling/exporter/test_http.py
--- a/tests/profiling/exporter/test_http.py
+++ b/tests/profiling/exporter/test_http.py
@@ -164,6 +164,7 @@ def test_wrong_api_key(endpoint_test_server):
     assert isinstance(e, error.HTTPError)
     assert e.code == 400
     assert e.reason == "Wrong API Key"
+    assert str(t.value) == "Unable to upload profile: Server returned 400, check your API key"
 
 
 def test_export(endpoint_test_server):