Skip to content

Conversation

amirdaraby
Copy link

Description

This PR fixes #7246
where the Admin API /load endpoint returned 200 OK instead of 400 Bad Request when an invalid Caddyfile also produced adapter warnings.

The problem happened because the warnings were written to the response first, which made http.ResponseWriter default to 200 OK. As a result, the response contained two separate JSON objects (warnings + error) stuck together, which was invalid JSON.

Changes

  • add "warnings" as valid JSON structure alongside to "errors" in JSON response.
  • Make sure the correct status code (400 Bad Request) is returned for invalid configs.

Behavior Before

curl -v --unix-socket /run/uncloud/caddy/admin.sock \
  -H "Content-Type: text/caddyfile" \
  --data-binary @Caddyfile \
  http://localhost/load
*   Trying /run/uncloud/caddy/admin.sock:0...
* Connected to localhost (/run/uncloud/caddy/admin.sock) port 0
* using HTTP/1.x
> POST /load HTTP/1.1
> Host: localhost
> User-Agent: curl/8.14.1
> Accept: */*
> Content-Type: text/caddyfile
> Content-Length: 81
> 
* upload completely sent off: 81 bytes
< HTTP/1.1 200 OK
< Date: Fri, 19 Sep 2025 15:21:18 GMT
< Content-Length: 336
< Content-Type: text/plain; charset=utf-8
< Connection: close
< 
[{"file":"Caddyfile","line":2,"message":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies"}]{"error":"loading config: loading new config: loading http app module: provision http: getting tls app: loading tls app module: provision tls: loading certificates: open cert.pem: no such file or directory"}
* shutting down connection #0

Behavior After

curl -v --unix-socket /run/uncloud/caddy/admin.sock \
  -H "Content-Type: text/caddyfile" \
  --data-binary @Caddyfile \
  http://localhost/load
*   Trying /run/uncloud/caddy/admin.sock:0...
* Connected to localhost (/run/uncloud/caddy/admin.sock) port 0
* using HTTP/1.x
> POST /load HTTP/1.1
> Host: localhost
> User-Agent: curl/8.14.1
> Accept: */*
> Content-Type: text/caddyfile
> Content-Length: 81
> 
* upload completely sent off: 81 bytes
< HTTP/1.1 400 Bad Request
< Date: Fri, 19 Sep 2025 15:26:41 GMT
< Content-Length: 348
< Content-Type: text/plain; charset=utf-8
< Connection: close
< 
{"error":"loading config: loading new config: loading http app module: provision http: getting tls app: loading tls app module: provision tls: loading certificates: open cert.pem: no such file or directory","warnings":[{"file":"Caddyfile","line":2,"message":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies"}]}
* shutting down connection #0

@CLAassistant
Copy link

CLAassistant commented Sep 19, 2025

CLA assistant check
All committers have signed the CLA.

Copy link
Member

@mholt mholt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thank you. Although, we should still probably return caddy.APIError if possible?

@amirdaraby
Copy link
Author

I agree. I'll update it so warnings also return caddy.APIError, to make sure error responses follow the same format.

@amirdaraby
Copy link
Author

I moved Warning into a separate package so it can be reused in caddy.APIError without causing import cycles. Now /load returns a proper APIError with both error and warnings fields.

@mohammed90
Copy link
Member

mohammed90 commented Oct 4, 2025

I moved Warning into a separate package so it can be reused in caddy.APIError without causing import cycles. Now /load returns a proper APIError with both error and warnings fields.

This breaks every single config adapter in the ecosystem. That's more than 1. We can't break that many.

I wonder if Go type aliases may be helpful in this instance though.

@amirdaraby amirdaraby force-pushed the fix/admin-warnings-in-response branch from b7f1368 to 106f1ec Compare October 4, 2025 17:02
@amirdaraby amirdaraby closed this Oct 4, 2025
@amirdaraby amirdaraby force-pushed the fix/admin-warnings-in-response branch from 04ac46e to 39ace45 Compare October 4, 2025 17:04
@mohammed90
Copy link
Member

Did you mean to do that??

@amirdaraby amirdaraby reopened this Oct 4, 2025
@amirdaraby
Copy link
Author

@mohammed90 Thanks for the suggestion, it was really helpful. I updated the code accordingly.

@amirdaraby
Copy link
Author

amirdaraby commented Oct 4, 2025

I think it would make sense to have another response type for success responses with warnings.

check these lines:
https://github.com/caddyserver/caddy/blob/master/caddyconfig/load.go#L165

https://github.com/caddyserver/caddy/pull/7267/files#diff-0c8bfec4802d5d401d81e1e2e4867055a211b8cf5a404c7ae16ef2e8419cb98fR132

I can add this as well if that’s okay.

@francislavoie
Copy link
Member

You mean just extract that (warnings+result) type out to an exported type instead of being inlined? I guess that's fine, but is there an actual usecase for doing that? What code would directly need that type definition published to use it?

@francislavoie francislavoie added this to the v2.11.0 milestone Oct 16, 2025
@amirdaraby
Copy link
Author

amirdaraby commented Oct 16, 2025

@francislavoie Yes it's possible to get a 200 OK status with warnings,

for example i called /load api with following caddyfile and result was 200 OK but there is warning about my file formatting.

Caddyfile

:8080 {
	respond "Hello, world"
}

API behavior (in my branch)

curl -v --unix-socket /run/uncloud/caddy/admin.sock \
  -H "Content-Type: text/caddyfile" \
  --data-binary @Caddyfile \
  http://localhost/load
*   Trying /run/uncloud/caddy/admin.sock:0...
* Connected to localhost (/run/uncloud/caddy/admin.sock) port 0
* using HTTP/1.x
> POST /load HTTP/1.1
> Host: localhost
> User-Agent: curl/8.14.1
> Accept: */*
> Content-Type: text/caddyfile
> Content-Length: 34
>
* upload completely sent off: 34 bytes
< HTTP/1.1 200 OK
< Date: Thu, 16 Oct 2025 13:51:32 GMT
< Content-Length: 142
< Content-Type: text/plain; charset=utf-8
< Connection: close
<
{"warnings":[{"file":"Caddyfile","line":2,"message":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies"}]}
* shutting down connection #0

i checked the codebase and inline type for result+warning only defined and used in these lines.

https://github.com/caddyserver/caddy/blob/master/caddyconfig/load.go#L165

https://github.com/caddyserver/caddy/pull/7267/files#diff-0c8bfec4802d5d401d81e1e2e4867055a211b8cf5a404c7ae16ef2e8419cb98fR132 (my PR)

i can work on adding an exported response type to handle these cases if you want.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Admin API /load returns 200 OK for invalid Caddyfile when it's not formatted with 'caddy fmt'

5 participants