{"html_url": "https://github.com/simonw/datasette/issues/1949#issuecomment-1352378370", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1949", "id": 1352378370, "node_id": "IC_kwDOBm6k_c5Qm6gC", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-15T00:02:08Z", "updated_at": "2022-12-15T00:04:54Z", "author_association": "OWNER", "body": "I fixed this issue to help research this further:\r\n- https://github.com/simonw/datasette-ripgrep/issues/26\r\n\r\nNow this search works:\r\n\r\n\r\n\r\nI wish I had this feature!\r\n- https://github.com/simonw/datasette-ripgrep/issues/24\r\n\r\nLooks like I have both `_error()` and `_errors()` functions in there!\r\n\r\n\"image\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1493471221, "label": "`.json` errors should be returned as JSON"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1949#issuecomment-1352411327", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1949", "id": 1352411327, "node_id": "IC_kwDOBm6k_c5QnCi_", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-15T00:46:27Z", "updated_at": "2022-12-15T00:46:27Z", "author_association": "OWNER", "body": "I got this far:\r\n```diff\r\ndiff --git a/datasette/handle_exception.py b/datasette/handle_exception.py\r\nindex 8b7e83e3..31d41e00 100644\r\n--- a/datasette/handle_exception.py\r\n+++ b/datasette/handle_exception.py\r\n@@ -54,7 +54,17 @@ def handle_exception(datasette, request, exception):\r\n headers = {}\r\n if datasette.cors:\r\n add_cors_headers(headers)\r\n- if request.path.split(\"?\")[0].endswith(\".json\"):\r\n+ # Return JSON error under certain conditions\r\n+ should_return_json = (\r\n+ # URL ends in .json\r\n+ request.path.split(\"?\")[0].endswith(\".json\")\r\n+ or\r\n+ # Hints from incoming request headers\r\n+ request.headers.get(\"content-type\") == \"application/json\"\r\n+ or \"application/json\" in request.headers.get(\"accept\", \"\")\r\n+ )\r\n+ breakpoint()\r\n+ if should_return_json:\r\n return Response.json(info, status=status, headers=headers)\r\n else:\r\n template = datasette.jinja_env.select_template(templates)\r\ndiff --git a/tests/test_api_write.py b/tests/test_api_write.py\r\nindex f27d143f..982543a6 100644\r\n--- a/tests/test_api_write.py\r\n+++ b/tests/test_api_write.py\r\n@@ -1140,6 +1140,38 @@ async def test_create_table_permissions(\r\n assert data[\"errors\"] == expected_errors\r\n \r\n \r\n+@pytest.mark.asyncio\r\n+@pytest.mark.parametrize(\r\n+ \"headers,expect_json\",\r\n+ (\r\n+ ({}, False),\r\n+ ({\"Accept\": \"text/html\"}, True),\r\n+ ({\"Accept\": \"application/json\"}, True),\r\n+ ({\"Content-Type\": \"application/json\"}, True),\r\n+ ({\"Accept\": \"application/json, text/plain, */*\"}, True),\r\n+ ({\"Content-Type\": \"application/json\"}, True),\r\n+ ({\"accept\": \"application/json, text/plain, */*\"}, True),\r\n+ ({\"content-type\": \"application/json\"}, True),\r\n+ ),\r\n+)\r\n+async def test_permission_errors_html_and_json(ds_write, headers, expect_json):\r\n+ request_headers = {\"Authorization\": \"Bearer bad_token\"}\r\n+ request_headers.update(headers)\r\n+ response = await ds_write.client.post(\r\n+ \"/data/-/create\",\r\n+ json={},\r\n+ headers=request_headers,\r\n+ )\r\n+ assert response.status_code == 403\r\n+ if expect_json:\r\n+ data = response.json()\r\n+ assert data[\"ok\"] is False\r\n+ assert data[\"errors\"] == [\"Permission denied\"]\r\n+ else:\r\n+ assert response.headers[\"Content-Type\"] == \"text/html; charset=utf-8\"\r\n+ assert \"Permission denied\" in response.text\r\n+\r\n+\r\n @pytest.mark.asyncio\r\n @pytest.mark.parametrize(\r\n \"input,expected_rows_after\",\r\n```\r\nThen decided I would punt this until the next milestone.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1493471221, "label": "`.json` errors should be returned as JSON"}, "performed_via_github_app": null}