kubernetes/kops · error
ReadOnlyError
ReadOnlyError
Error message
AssetPath is read-only
What it means
AssetPath is a read-only vfs.Path backed by Go embed.FS content (embedded cloudup templates). Because embedded filesystems cannot be modified at runtime, all write operations (WriteFile, CreateFile, Remove, RemoveAll) unconditionally return the shared package-level ReadOnlyError sentinel.
Source
Thrown at upup/models/vfs.go:31
See the License for the specific language governing permissions and
limitations under the License.
*/
package models
import (
"context"
"embed"
"errors"
"io"
"io/fs"
"os"
"path"
"k8s.io/kops/util/pkg/vfs"
)
var ReadOnlyError = errors.New("AssetPath is read-only")
//go:embed cloudup
var content embed.FS
type AssetPath struct {
location string
}
var _ vfs.Path = &AssetPath{}
func NewAssetPath(location string) *AssetPath {
a := &AssetPath{
location: location,
}
return a
}
func (p *AssetPath) Join(relativePath ...string) vfs.Path {View on GitHub (pinned to 4c8573c808)
Solutions
- Do not write to AssetPath; read the embedded content and write to a mutable vfs.Path (e.g. vfs.Context.BuildFile or os.WriteFile) instead
- Check writability before writing: only call WriteFile on paths that are not AssetPath (e.g. via a type switch on *AssetPath)
- If you need to modify the templates, edit the files under upup/models/cloudup in the source tree and rebuild
- Use RemoveAll/Remove only on writable backing stores; skip cleanup for embedded assets
Example fix
// before
if err := path.WriteFile(ctx, bytes.NewReader(data), vfs.ACLPrivate); err != nil { return err } // AssetPath is read-only
// after
switch p := path.(type) {
case *models.AssetPath:
// embedded assets are immutable; write elsewhere
return os.WriteFile("/tmp/cloudup.yaml", data, 0644)
default:
return path.WriteFile(ctx, bytes.NewReader(data), vfs.ACLPrivate)
} Defensive patterns
Strategy: type-guard
Validate before calling
func writable(p vfs.Path) bool { _, readOnly := p.(*models.AssetPath); return !readOnly }
if !writable(path) { /* write to a mutable path instead */ } Type guard
func isAssetPath(p vfs.Path) (*models.AssetPath, bool) {
ap, ok := p.(*models.AssetPath)
return ap, ok
} Try / catch
if err := p.WriteFile(ctx, r, acl); err != nil {
if errors.Is(err, models.ReadOnlyError) {
// fall back to writing outside the embedded FS
} else { return err }
} Prevention
- Never assume every vfs.Path is writable; check the concrete type before write ops
- Keep embedded assets immutable and copy them to writable storage when mutation is needed
- Route all writes through a helper that rejects *AssetPath early
When it happens
Trigger: Calling WriteFile, CreateFile, Remove, or RemoveAll on an AssetPath obtained via vfs.Context, e.g. attempting to overwrite an embedded template or add a new file under the embedded cloudup tree.
Common situations: Code that treats all vfs.Path values uniformly and writes templated cluster assets back to the same path it read them from; tests or tooling that try to mutate embedded assets; refactoring code that previously used a real (filesystem/S3) VFS path to use embedded assets.
Related errors
- unexpected kind for cluster, got %T, want kops.Cluster
- method ConfigBaseFor not supported in server-side client
- ResourceVersion not supported in InstanceGroupVFS::Get
- InstanceGroups::Create not supported for server-side client
- InstanceGroups::Update not supported for server-side client
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/dff33dbbec881f07.
Report an issue: GitHub.