{"record":{"id":"a04ca8106307f370","repo":"tailscale/tailscale","slug":"s-s","errorCode":null,"errorMessage":"%s: %s","messagePattern":"%s: %s","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/local/local.go","lineNumber":877,"sourceCode":"// The name parameter is the original filename, not escaped.\nfunc (lc *Client) PushFile(ctx context.Context, target tailcfg.StableNodeID, size int64, name string, r io.Reader) error {\n\treq, err := http.NewRequestWithContext(ctx, \"PUT\", \"http://\"+apitype.LocalAPIHost+\"/localapi/v0/file-put/\"+string(target)+\"/\"+url.PathEscape(name), r)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif size != -1 {\n\t\treq.ContentLength = size\n\t}\n\tres, err := lc.doLocalRequestNiceError(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif res.StatusCode == 200 {\n\t\tio.Copy(io.Discard, res.Body)\n\t\treturn nil\n\t}\n\tall, _ := io.ReadAll(res.Body)\n\treturn bestError(fmt.Errorf(\"%s: %s\", res.Status, all), all)\n}\n\n// CheckIPForwarding asks the local Tailscale daemon whether it looks like the\n// machine is properly configured to forward IP packets as a subnet router\n// or exit node.\n//\n// API maturity: this method is not considered a stable API and is\n// subject to change between releases.\nfunc (lc *Client) CheckIPForwarding(ctx context.Context) error {\n\tif !buildfeatures.HasAdvertiseRoutes {\n\t\treturn nil\n\t}\n\tbody, err := lc.get200(ctx, \"/localapi/v0/check-ip-forwarding\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar jres struct {\n\t\tWarning string","sourceCodeStart":859,"sourceCodeEnd":895,"githubUrl":"https://github.com/tailscale/tailscale/blob/cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042/client/local/local.go#L859-L895","documentation":"Returned by Client.PushFile (client/local/local.go:877) when PUTting a file to /localapi/v0/files/<target> does not return 200. The status and full body are combined into an error passed through bestError(), which prefers a structured message from the body when one exists. A 200 with an error body is handled elsewhere (bestError path after copying), so this line is strictly the non-OK status case.","triggerScenarios":"Pushing to a target that is not a valid peer (unknown key/IP); Taildrop file sending disabled by policy on the tailnet or for the target node; the target node offline or not accepting files; permission denial on the local API.","commonSituations":"Sending files to devices that have file receiving off; policy changes disabling Taildrop mid-integration; stale FileTargets results used after a peer went away.","solutions":["Read the body in the error — it states whether it is policy, target validity, or state","Verify the target with FileTargets and confirm it is online and file-capable","Enable Taildrop/file receiving for the target (admin policy + node settings) before pushing","Retry once after re-fetching targets if the failure looks transient"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Confirm the target accepts files before pushing.\nfts, err := lc.FileTargets(ctx)\nif err != nil {\n\treturn err\n}\nok := false\nfor _, ft := range fts {\n\tif ft.Node.Key == targetKey {\n\t\tok = true\n\t\tbreak\n\t}\n}\nif !ok {\n\treturn fmt.Errorf(\"target %x not file-capable or offline\", targetKey)\n}","typeGuard":null,"tryCatchPattern":"if err := lc.PushFile(ctx, target, size, r); err != nil {\n\tif strings.Contains(err.Error(), \"not enabled\") || strings.Contains(err.Error(), \"denied\") {\n\t\treturn errors.New(\"Taildrop disabled by policy or target settings\")\n\t}\n\treturn err // bestError already prefers the structured daemon message\n}","preventionTips":["Check FileTargets right before the push, not from a cached snapshot","Verify Taildrop is permitted by tailnet policy and enabled on the target device","Surface the daemon's body text to users — it distinguishes policy vs. state failures"],"tags":["go","tailscale","local-api","taildrop","file-upload","policy"],"backgroundTag":null,"analyzedSha":"cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042","analyzedAt":"2026-08-15T19:58:31.583Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}